jeecguniapp1.0
This commit is contained in:
parent
bd1d2cd6d2
commit
cfe8db7730
106
.commitlintrc.cjs
Normal file
106
.commitlintrc.cjs
Normal file
@ -0,0 +1,106 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const { execSync } = require('child_process')
|
||||
|
||||
const scopes = fs
|
||||
.readdirSync(path.resolve(__dirname, 'src'), { withFileTypes: true })
|
||||
.filter((dirent) => dirent.isDirectory())
|
||||
.map((dirent) => dirent.name.replace(/s$/, ''))
|
||||
|
||||
// precomputed scope
|
||||
const scopeComplete = execSync('git status --porcelain || true')
|
||||
.toString()
|
||||
.trim()
|
||||
.split('\n')
|
||||
.find((r) => ~r.indexOf('M src'))
|
||||
?.replace(/(\/)/g, '%%')
|
||||
?.match(/src%%((\w|-)*)/)?.[1]
|
||||
?.replace(/s$/, '')
|
||||
|
||||
module.exports = {
|
||||
ignores: [(commit) => commit.includes('init')],
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
rules: {
|
||||
'body-leading-blank': [2, 'always'],
|
||||
'footer-leading-blank': [1, 'always'],
|
||||
'header-max-length': [2, 'always', 108],
|
||||
'subject-empty': [2, 'never'],
|
||||
'type-empty': [2, 'never'],
|
||||
'subject-case': [0],
|
||||
'type-enum': [
|
||||
2,
|
||||
'always',
|
||||
[
|
||||
'feat',
|
||||
'fix',
|
||||
'perf',
|
||||
'style',
|
||||
'docs',
|
||||
'test',
|
||||
'refactor',
|
||||
'build',
|
||||
'ci',
|
||||
'chore',
|
||||
'revert',
|
||||
'wip',
|
||||
'workflow',
|
||||
'types',
|
||||
'release',
|
||||
],
|
||||
],
|
||||
},
|
||||
prompt: {
|
||||
/** @use `pnpm commit :f` */
|
||||
alias: {
|
||||
f: 'docs: fix typos',
|
||||
r: 'docs: update README',
|
||||
s: 'style: update code format',
|
||||
b: 'build: bump dependencies',
|
||||
c: 'chore: update config',
|
||||
},
|
||||
customScopesAlign: !scopeComplete ? 'top' : 'bottom',
|
||||
defaultScope: scopeComplete,
|
||||
scopes: [...scopes, 'mock'],
|
||||
allowEmptyIssuePrefixs: false,
|
||||
allowCustomIssuePrefixs: false,
|
||||
|
||||
// English
|
||||
typesAppend: [
|
||||
{ value: 'wip', name: 'wip: work in process' },
|
||||
{ value: 'workflow', name: 'workflow: workflow improvements' },
|
||||
{ value: 'types', name: 'types: type definition file changes' },
|
||||
],
|
||||
|
||||
// 中英文对照版
|
||||
// messages: {
|
||||
// type: '选择你要提交的类型 :',
|
||||
// scope: '选择一个提交范围 (可选):',
|
||||
// customScope: '请输入自定义的提交范围 :',
|
||||
// subject: '填写简短精炼的变更描述 :\n',
|
||||
// body: '填写更加详细的变更描述 (可选)。使用 "|" 换行 :\n',
|
||||
// breaking: '列举非兼容性重大的变更 (可选)。使用 "|" 换行 :\n',
|
||||
// footerPrefixsSelect: '选择关联issue前缀 (可选):',
|
||||
// customFooterPrefixs: '输入自定义issue前缀 :',
|
||||
// footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
|
||||
// confirmCommit: '是否提交或修改commit ?',
|
||||
// },
|
||||
// types: [
|
||||
// { value: 'feat', name: 'feat: 新增功能' },
|
||||
// { value: 'fix', name: 'fix: 修复缺陷' },
|
||||
// { value: 'docs', name: 'docs: 文档变更' },
|
||||
// { value: 'style', name: 'style: 代码格式' },
|
||||
// { value: 'refactor', name: 'refactor: 代码重构' },
|
||||
// { value: 'perf', name: 'perf: 性能优化' },
|
||||
// { value: 'test', name: 'test: 添加疏漏测试或已有测试改动' },
|
||||
// { value: 'build', name: 'build: 构建流程、外部依赖变更 (如升级 npm 包、修改打包配置等)' },
|
||||
// { value: 'ci', name: 'ci: 修改 CI 配置、脚本' },
|
||||
// { value: 'revert', name: 'revert: 回滚 commit' },
|
||||
// { value: 'chore', name: 'chore: 对构建过程或辅助工具和库的更改 (不影响源文件、测试用例)' },
|
||||
// { value: 'wip', name: 'wip: 正在开发中' },
|
||||
// { value: 'workflow', name: 'workflow: 工作流程改进' },
|
||||
// { value: 'types', name: 'types: 类型定义文件修改' },
|
||||
// ],
|
||||
// emptyScopesAlias: 'empty: 不填写',
|
||||
// customScopesAlias: 'custom: 自定义',
|
||||
},
|
||||
}
|
||||
13
.editorconfig
Normal file
13
.editorconfig
Normal file
@ -0,0 +1,13 @@
|
||||
root = true
|
||||
|
||||
[*] # 表示所有文件适用
|
||||
charset = utf-8 # 设置文件字符集为 utf-8
|
||||
indent_style = space # 缩进风格(tab | space)
|
||||
indent_size = 2 # 缩进大小
|
||||
end_of_line = lf # 控制换行类型(lf | cr | crlf)
|
||||
trim_trailing_whitespace = true # 去除行首的任意空白字符
|
||||
insert_final_newline = true # 始终在文件末尾插入一个新行
|
||||
|
||||
[*.md] # 表示仅 md 文件适用以下规则
|
||||
max_line_length = off # 关闭最大行长度限制
|
||||
trim_trailing_whitespace = false # 关闭末尾空格修剪
|
||||
1
.eslintignore
Normal file
1
.eslintignore
Normal file
@ -0,0 +1 @@
|
||||
src/uni_modules/
|
||||
100
.eslintrc-auto-import.json
Normal file
100
.eslintrc-auto-import.json
Normal file
@ -0,0 +1,100 @@
|
||||
{
|
||||
"globals": {
|
||||
"Component": true,
|
||||
"ComponentPublicInstance": true,
|
||||
"ComputedRef": true,
|
||||
"DirectiveBinding": true,
|
||||
"EffectScope": true,
|
||||
"ExtractDefaultPropTypes": true,
|
||||
"ExtractPropTypes": true,
|
||||
"ExtractPublicPropTypes": true,
|
||||
"InjectionKey": true,
|
||||
"MaybeRef": true,
|
||||
"MaybeRefOrGetter": true,
|
||||
"PropType": true,
|
||||
"Ref": true,
|
||||
"VNode": true,
|
||||
"WritableComputedRef": true,
|
||||
"computed": true,
|
||||
"createApp": true,
|
||||
"customRef": true,
|
||||
"defineAsyncComponent": true,
|
||||
"defineComponent": true,
|
||||
"effectScope": true,
|
||||
"getCurrentInstance": true,
|
||||
"getCurrentScope": true,
|
||||
"h": true,
|
||||
"inject": true,
|
||||
"isProxy": true,
|
||||
"isReactive": true,
|
||||
"isReadonly": true,
|
||||
"isRef": true,
|
||||
"markRaw": true,
|
||||
"nextTick": true,
|
||||
"onActivated": true,
|
||||
"onAddToFavorites": true,
|
||||
"onBackPress": true,
|
||||
"onBeforeMount": true,
|
||||
"onBeforeUnmount": true,
|
||||
"onBeforeUpdate": true,
|
||||
"onDeactivated": true,
|
||||
"onError": true,
|
||||
"onErrorCaptured": true,
|
||||
"onHide": true,
|
||||
"onLaunch": true,
|
||||
"onLoad": true,
|
||||
"onMounted": true,
|
||||
"onNavigationBarButtonTap": true,
|
||||
"onNavigationBarSearchInputChanged": true,
|
||||
"onNavigationBarSearchInputClicked": true,
|
||||
"onNavigationBarSearchInputConfirmed": true,
|
||||
"onNavigationBarSearchInputFocusChanged": true,
|
||||
"onPageNotFound": true,
|
||||
"onPageScroll": true,
|
||||
"onPullDownRefresh": true,
|
||||
"onReachBottom": true,
|
||||
"onReady": true,
|
||||
"onRenderTracked": true,
|
||||
"onRenderTriggered": true,
|
||||
"onResize": true,
|
||||
"onScopeDispose": true,
|
||||
"onServerPrefetch": true,
|
||||
"onShareAppMessage": true,
|
||||
"onShareTimeline": true,
|
||||
"onShow": true,
|
||||
"onTabItemTap": true,
|
||||
"onThemeChange": true,
|
||||
"onUnhandledRejection": true,
|
||||
"onUnload": true,
|
||||
"onUnmounted": true,
|
||||
"onUpdated": true,
|
||||
"onWatcherCleanup": true,
|
||||
"provide": true,
|
||||
"reactive": true,
|
||||
"readonly": true,
|
||||
"ref": true,
|
||||
"resolveComponent": true,
|
||||
"shallowReactive": true,
|
||||
"shallowReadonly": true,
|
||||
"shallowRef": true,
|
||||
"toRaw": true,
|
||||
"toRef": true,
|
||||
"toRefs": true,
|
||||
"toValue": true,
|
||||
"triggerRef": true,
|
||||
"unref": true,
|
||||
"useAttrs": true,
|
||||
"useCssModule": true,
|
||||
"useCssVars": true,
|
||||
"useId": true,
|
||||
"useModel": true,
|
||||
"useRequest": true,
|
||||
"useSlots": true,
|
||||
"useTemplateRef": true,
|
||||
"useUpload": true,
|
||||
"watch": true,
|
||||
"watchEffect": true,
|
||||
"watchPostEffect": true,
|
||||
"watchSyncEffect": true
|
||||
}
|
||||
}
|
||||
97
.eslintrc.cjs
Normal file
97
.eslintrc.cjs
Normal file
@ -0,0 +1,97 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
node: true,
|
||||
},
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:vue/vue3-essential',
|
||||
// eslint-plugin-import 插件, @see https://www.npmjs.com/package/eslint-plugin-import
|
||||
'plugin:import/recommended',
|
||||
// eslint-config-airbnb-base 插件 已经改用 eslint-config-standard 插件
|
||||
'standard',
|
||||
// 1. 接入 prettier 的规则
|
||||
'prettier',
|
||||
'plugin:prettier/recommended',
|
||||
'./.eslintrc-auto-import.json',
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
files: ['.eslintrc.{js,cjs}'],
|
||||
parserOptions: {
|
||||
sourceType: 'script',
|
||||
},
|
||||
},
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
parser: '@typescript-eslint/parser',
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'vue',
|
||||
// 2. 加入 prettier 的 eslint 插件
|
||||
'prettier',
|
||||
// eslint-import-resolver-typescript 插件,@see https://www.npmjs.com/package/eslint-import-resolver-typescript
|
||||
'import',
|
||||
],
|
||||
rules: {
|
||||
// 3. 注意要加上这一句,开启 prettier 自动修复的功能
|
||||
'prettier/prettier': 'error',
|
||||
// turn on errors for missing imports
|
||||
'import/no-unresolved': 'off',
|
||||
// 对后缀的检测,否则 import 一个ts文件也会报错,需要手动添加'.ts', 增加了下面的配置后就不用了
|
||||
'import/extensions': [
|
||||
'error',
|
||||
'ignorePackages',
|
||||
{ js: 'never', jsx: 'never', ts: 'never', tsx: 'never' },
|
||||
],
|
||||
// 只允许1个默认导出,关闭,否则不能随意export xxx
|
||||
'import/prefer-default-export': ['off'],
|
||||
'no-console': ['off'],
|
||||
// 'no-unused-vars': ['off'],
|
||||
// '@typescript-eslint/no-unused-vars': ['off'],
|
||||
// 解决vite.config.ts报错问题
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
'no-plusplus': 'off',
|
||||
'no-shadow': 'off',
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'no-undef': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
// 避免 `eslint` 对于 `typescript` 函数重载的误报
|
||||
'no-redeclare': 'off',
|
||||
'@typescript-eslint/no-redeclare': 'error',
|
||||
},
|
||||
// eslint-import-resolver-typescript 插件,@see https://www.npmjs.com/package/eslint-import-resolver-typescript
|
||||
settings: {
|
||||
'import/parsers': {
|
||||
'@typescript-eslint/parser': ['.ts', '.tsx'],
|
||||
},
|
||||
'import/resolver': {
|
||||
typescript: {},
|
||||
},
|
||||
},
|
||||
globals: {
|
||||
$t: true,
|
||||
uni: true,
|
||||
UniApp: true,
|
||||
wx: true,
|
||||
WechatMiniprogram: true,
|
||||
getCurrentPages: true,
|
||||
UniHelper: true,
|
||||
Page: true,
|
||||
App: true,
|
||||
NodeJS: true,
|
||||
},
|
||||
}
|
||||
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.js linguist-language=vue
|
||||
*.css linguist-language=vue
|
||||
*.ts linguist-language=vue
|
||||
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
node_modules
|
||||
dist
|
||||
.npmrc
|
||||
.cache
|
||||
|
||||
## ide
|
||||
**/.idea
|
||||
*.iml
|
||||
|
||||
## backend
|
||||
**/target
|
||||
**/logs
|
||||
**/dist
|
||||
|
||||
## front
|
||||
/yarn-error.log
|
||||
12
.prettierignore
Normal file
12
.prettierignore
Normal file
@ -0,0 +1,12 @@
|
||||
# unplugin-auto-import 生成的类型文件,每次提交都改变,所以加入这里吧,与 .gitignore 配合使用
|
||||
auto-import.d.ts
|
||||
|
||||
# vite-plugin-uni-pages 生成的类型文件,每次切换分支都一堆不同的,所以直接 .gitignore
|
||||
uni-pages.d.ts
|
||||
|
||||
# 插件生成的文件
|
||||
src/pages.json
|
||||
src/manifest.json
|
||||
|
||||
# 忽略自动生成文件
|
||||
src/service/app/**
|
||||
19
.prettierrc.cjs
Normal file
19
.prettierrc.cjs
Normal file
@ -0,0 +1,19 @@
|
||||
// @see https://prettier.io/docs/en/options
|
||||
module.exports = {
|
||||
singleQuote: true,
|
||||
printWidth: 100,
|
||||
tabWidth: 2,
|
||||
useTabs: false,
|
||||
semi: false,
|
||||
trailingComma: 'all',
|
||||
endOfLine: 'auto',
|
||||
htmlWhitespaceSensitivity: 'ignore',
|
||||
overrides: [
|
||||
{
|
||||
files: '*.json',
|
||||
options: {
|
||||
trailingComma: 'none',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
1
.stylelintignore
Normal file
1
.stylelintignore
Normal file
@ -0,0 +1 @@
|
||||
src/uni_modules/
|
||||
58
.stylelintrc.cjs
Normal file
58
.stylelintrc.cjs
Normal file
@ -0,0 +1,58 @@
|
||||
// .stylelintrc.cjs
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
// stylelint-config-standard 替换成了更宽松的 stylelint-config-recommended
|
||||
'stylelint-config-recommended',
|
||||
// stylelint-config-standard-scss 替换成了更宽松的 stylelint-config-recommended-scss
|
||||
'stylelint-config-recommended-scss',
|
||||
'stylelint-config-recommended-vue/scss',
|
||||
'stylelint-config-html/vue',
|
||||
'stylelint-config-recess-order',
|
||||
],
|
||||
plugins: ['stylelint-prettier'],
|
||||
overrides: [
|
||||
// 扫描 .vue/html 文件中的<style>标签内的样式
|
||||
{
|
||||
files: ['**/*.{vue,html}'],
|
||||
customSyntax: 'postcss-html',
|
||||
},
|
||||
{
|
||||
files: ['**/*.{css,scss}'],
|
||||
customSyntax: 'postcss-scss',
|
||||
},
|
||||
],
|
||||
// 自定义规则
|
||||
rules: {
|
||||
'prettier/prettier': true,
|
||||
// 允许 global 、export 、v-deep等伪类
|
||||
'selector-pseudo-class-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignorePseudoClasses: ['global', 'export', 'v-deep', 'deep'],
|
||||
},
|
||||
],
|
||||
'unit-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignoreUnits: ['rpx'],
|
||||
},
|
||||
],
|
||||
// 处理小程序page标签不认识的问题
|
||||
'selector-type-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignoreTypes: ['page'],
|
||||
},
|
||||
],
|
||||
'comment-empty-line-before': 'never', // never|always|always-multi-line|never-multi-line
|
||||
'custom-property-empty-line-before': 'never',
|
||||
'no-empty-source': null,
|
||||
'comment-no-empty': null,
|
||||
'no-duplicate-selectors': null,
|
||||
'scss/comment-no-empty': null,
|
||||
'selector-class-pattern': null,
|
||||
'font-family-no-missing-generic-family-keyword': null,
|
||||
},
|
||||
}
|
||||
3
.vite/deps_temp_b56c6fb9/package.json
Normal file
3
.vite/deps_temp_b56c6fb9/package.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
214
LICENSE
Normal file
214
LICENSE
Normal file
@ -0,0 +1,214 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (c) 2019 <a href="http://www.jeecg.com">Jeecg Boot</a> All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
In any case, you must not make any such use of this software as to develop software which may be considered competitive with this software.
|
||||
|
||||
JeecgUniapp 是由 北京国炬信息技术有限公司 发行的软件。 总部位于北京,地址:中国·北京·朝阳区科荟前街1号院奥林佳泰大厦。邮箱:jeecgos@163.com
|
||||
本软件受适用的国家软件著作权法(包括国际条约)和开源协议 双重保护许可。
|
||||
|
||||
开源协议中文释意如下:
|
||||
1.JeecgUniapp开源版本无任何限制,在遵循本开源协议条款下,允许商用使用,不会造成侵权行为。
|
||||
2.允许基于本平台软件开展业务系统开发。
|
||||
3.在任何情况下,您不得使用本软件开发可能被认为与本软件竞争的软件。
|
||||
|
||||
最终解释权归:http://www.jeecg.com
|
||||
188
README.md
188
README.md
@ -1,2 +1,188 @@
|
||||
# JeecgUniapp
|
||||
# 项目介绍
|
||||
|
||||
JeecgUniapp 是`JeecgBoot低代码平台`的配套`APP移动框架`,项目采用 Uniapp、Vue3.0、Vite、 Wot-design-uni、TypeScript 等最新技术栈,包括二次封装组件、路由拦截、请求拦截等功能。实现了与 `JeecgBoot` 完美对接:
|
||||
目前已经实现登录、用户信息、通讯录、公告、移动首页、九宫格、聊天、Online表单、仪表盘等功能,提供了丰富的组件!
|
||||
|
||||
当前最新版本: 3.0.0(发布日期:2025-03-10)
|
||||
|
||||
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
|
||||
[](http://www.jeecg.com)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
|
||||
|
||||
|
||||
### 视频介绍
|
||||
|
||||
[](https://www.bilibili.com/video/BV15QQeYoEfQ)
|
||||
|
||||
|
||||
### 新版特点
|
||||
- 一份代码多终端适配,小程序、H5、安卓、ios、鸿蒙Next。
|
||||
- 学习成本低、组件丰富、兼容性好、支持iframe嵌入。
|
||||
- 新版APP具备低代码能力,包括表单设计、仪表盘设计等。
|
||||
- 新版最大亮点是架构升级到 Vue3,适配鸿蒙 Next。
|
||||
- 支持使用 VSCode 和 IntelliJ IDEA 开发,不再必须 HBuilderX,这显著提升了开发体验和效率。
|
||||
- 支持低代码能力,例如Online表单可以在APP端展示和进行数据的添加与修改,仪表盘和大屏也支持移动端展示。
|
||||
|
||||
### 前端技术栈
|
||||
|
||||
- 前端 IDE 建议:Vscode、HBuilderX、Intellij IDEA
|
||||
- 最新技术栈:Uniapp + Vue3.0 + Vite + TypeScript + Wot-design-uni + pinia + unocss
|
||||
- 依赖管理:node、pnpm
|
||||
|
||||
### 环境要求
|
||||
|
||||
- 本地环境安装 node(18+)、pnpm (7.3+)
|
||||
|
||||
|
||||
|
||||
### 后台源码
|
||||
|
||||
- https://github.com/jeecgboot/jeecg-boot
|
||||
|
||||
### 技术交流
|
||||
|
||||
- 产品官网: [http://jeecg.com/appIndex](http://jeecg.com/appIndex)
|
||||
- 开发文档: [https://help.jeecg.com/uniapp3](https://help.jeecg.com/uniapp3)
|
||||
- 官方支持: 遇到BUG可以在github上 [发Issue](https://github.com/jeecgboot/jeecg-uniapp/issues/new)
|
||||
- QQ交流群: 716488839
|
||||
|
||||
### 快速启动
|
||||
|
||||
#### 配置接口地址
|
||||
|
||||
> 配置文件:`env/.env.development`
|
||||
|
||||
请把 http://localhost:8080/jeecg-boot 替换成自己地址,其他不用改。
|
||||
|
||||
```javascript
|
||||
// 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV = 'development';
|
||||
// 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE = false;
|
||||
// 是否开启sourcemap
|
||||
VITE_SHOW_SOURCEMAP = true;
|
||||
// 后台接口全路径地址(必填)
|
||||
VITE_SERVER_BASEURL = 'http://localhost:8080/jeecg-boot';
|
||||
```
|
||||
|
||||
#### 启动项目
|
||||
|
||||
执行命令安装依赖
|
||||
```
|
||||
pnpm i
|
||||
```
|
||||
运行启动命令,运行 `H5`
|
||||
|
||||
```
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
|
||||
### 新旧版本对比
|
||||
|
||||
| 特性 | 旧版 | 新版 |
|
||||
| -------------------- | -------------- |------------------------------------------------|
|
||||
| **技术栈** | Vue2,技术陈旧 | Vue3,现代化开发体验 |
|
||||
| **UI 框架** | 未集成 | 集成`wot-design-uni` |
|
||||
| **编辑器** | 仅限 HbuilderX | 支持 VSCode、Intellij IDEA 等主流编辑器 |
|
||||
| **鸿蒙** | 不支持 | 支持鸿蒙系统 |
|
||||
| **构建工具** | Webpack | Vite,构建更快 |
|
||||
| **Unocss 原子化** | 不支持 | 支持 Unocss 原子化 |
|
||||
| **TypeScript 支持** | 不支持 | 支持 TS,提供类型提示 |
|
||||
| **基础组件封装** | 较少 | 封装丰富组件(用户、部门、分类字典树、自定义树、popup、popupDict、导航组件等) |
|
||||
| **代码片段快捷创建** | 无 | 支持 v3 快速创建页面片段 |
|
||||
|
||||
### 入门必备
|
||||
|
||||
本项目需要一定前端基础知识,请确保掌握 Vue 的基础知识,以便能处理一些常见的问题。 建议在开发前先学一下以下内容,提前了解和学习这些知识,会对项目理解非常有帮助:
|
||||
|
||||
- [Vue3 文档](https://cn.vuejs.org)
|
||||
- [Uniapp](https://uniapp.dcloud.net.cn/)
|
||||
- [Wot-design-uni](https://wot-design-uni.cn)
|
||||
- [Unibest 文档](https://www.unibest.tech)
|
||||
- [TypeScript](https://www.typescriptlang.org)
|
||||
- [Es6](https://es6.ruanyifeng.com/)
|
||||
- [Vitejs](https://vitejs.dev)
|
||||
- [Pinia(vuex 替代方案)](https://pinia.esm.dev/introduction.html)
|
||||
- [Vue-RFCS](https://github.com/vuejs/rfcs)
|
||||
- [UnoCSS](https://unocss.dev)
|
||||
|
||||
|
||||
|
||||
|
||||
### 效果预览
|
||||
|
||||
- 基础功能
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
- 聊天功能
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
- Online表单
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
- 仪表盘
|
||||
|
||||

|
||||

|
||||
|
||||
- 动画展示
|
||||
|
||||

|
||||
|
||||
|
||||
### 功能模块
|
||||
```
|
||||
├─框架实现
|
||||
│ ├─APP开发框架搭建
|
||||
│ ├─登录对接
|
||||
│ ├─TOKEN接口机制
|
||||
│ ├─热更新\覆盖更新
|
||||
├─基础功能
|
||||
│ ├─菜单栏目
|
||||
│ ├─登录页面
|
||||
│ ├─移动首页
|
||||
│ ├─个人信息设置
|
||||
├─消息中心
|
||||
│ ├─通讯录
|
||||
│ ├─系统公告
|
||||
│ ├─消息推送
|
||||
│ ├─在线聊天
|
||||
├─低代码功能
|
||||
│ ├─Online表单(列表+表单渲染)
|
||||
│ ├─仪表盘(移动展示)
|
||||
├─示例代码
|
||||
│ ├─调用摄像头扫码(扫码)
|
||||
│ ├─获取地理位置(定位)
|
||||
├─新增组件
|
||||
│ ├─页面滚动
|
||||
│ ├─日历
|
||||
│ ├─时间选择
|
||||
│ ├─下拉选择
|
||||
│ ├─图片上传
|
||||
├─。。。
|
||||
```
|
||||
|
||||
29
env/.env
vendored
Normal file
29
env/.env
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
VITE_APP_TITLE = 'JeecgBoot-uniapp'
|
||||
VITE_APP_PORT = 9000
|
||||
|
||||
VITE_UNI_APPID = '__UNI__9F097F0'
|
||||
VITE_WX_APPID = 'wx8e287639924edb51'
|
||||
|
||||
# h5部署网站的base,配置到 manifest.config.ts 里的 h5.router.base
|
||||
VITE_APP_PUBLIC_BASE=/
|
||||
|
||||
VITE_SERVER_BASEURL = ''
|
||||
# 上传接口
|
||||
VITE_UPLOAD_BASEURL = ''
|
||||
|
||||
# 有些同学可能需要在微信小程序里面根据 develop、trial、release 分别设置上传地址,参考代码如下。
|
||||
# 下面的变量如果没有设置,会默认使用 VITE_SERVER_BASEURL or VITE_UPLOAD_BASEURL
|
||||
VITE_SERVER_BASEURL__WEIXIN_DEVELOP = ''
|
||||
VITE_SERVER_BASEURL__WEIXIN_TRIAL = ''
|
||||
VITE_SERVER_BASEURL__WEIXIN_RELEASE = ''
|
||||
|
||||
VITE_UPLOAD_BASEURL__WEIXIN_DEVELOP = ''
|
||||
VITE_UPLOAD_BASEURL__WEIXIN_TRIAL = ''
|
||||
VITE_UPLOAD_BASEURL__WEIXIN_RELEASE = ''
|
||||
|
||||
# h5是否需要配置代理
|
||||
VITE_APP_PROXY= false
|
||||
VITE_APP_PROXY_PREFIX = '/api'
|
||||
|
||||
# 是否启用mock (1.仅支持h5 2.启用必须要开启代理,否则生效。)
|
||||
VITE_USE_MOCK = true
|
||||
9
env/.env.development
vendored
Normal file
9
env/.env.development
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV = 'development'
|
||||
# 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE = false
|
||||
# 是否开启sourcemap
|
||||
VITE_SHOW_SOURCEMAP = true
|
||||
|
||||
#VITE_SERVER_BASEURL = 'http://127.0.0.1:8080/jeecg-boot'
|
||||
VITE_SERVER_BASEURL = 'https://36.112.48.190/jeecg-boot'
|
||||
8
env/.env.production
vendored
Normal file
8
env/.env.production
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV = 'development'
|
||||
# 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE = true
|
||||
# 是否开启sourcemap
|
||||
VITE_SHOW_SOURCEMAP = false
|
||||
|
||||
VITE_SERVER_BASEURL = 'https://api3.boot.jeecg.com'
|
||||
4
env/.env.test
vendored
Normal file
4
env/.env.test
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV = 'development'
|
||||
# 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE = false
|
||||
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 487 B |
26
index.html
Normal file
26
index.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!doctype html>
|
||||
<html build-time="%BUILD_TIME%">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
|
||||
<script>
|
||||
var coverSupport =
|
||||
'CSS' in window &&
|
||||
typeof CSS.supports === 'function' &&
|
||||
(CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||
(coverSupport ? ', viewport-fit=cover' : '') +
|
||||
'" />',
|
||||
)
|
||||
</script>
|
||||
<title>JeecgBoot-uniapp3</title>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
162
manifest.config.ts
Normal file
162
manifest.config.ts
Normal file
@ -0,0 +1,162 @@
|
||||
// manifest.config.ts
|
||||
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
|
||||
import path from 'node:path'
|
||||
import { loadEnv } from 'vite'
|
||||
|
||||
// 获取环境变量的范例
|
||||
const env = loadEnv(process.env.NODE_ENV!, path.resolve(process.cwd(), 'env'))
|
||||
const {
|
||||
VITE_APP_TITLE,
|
||||
VITE_UNI_APPID,
|
||||
VITE_WX_APPID,
|
||||
VITE_APP_PUBLIC_BASE,
|
||||
VITE_FALLBACK_LOCALE,
|
||||
} = env
|
||||
|
||||
export default defineManifestConfig({
|
||||
name: VITE_APP_TITLE,
|
||||
appid: VITE_UNI_APPID,
|
||||
description: '',
|
||||
versionName: '1.0.0',
|
||||
versionCode: '100',
|
||||
transformPx: false,
|
||||
locale: VITE_FALLBACK_LOCALE, // 'zh-Hans'
|
||||
/* 5+App特有相关 */
|
||||
'app-plus': {
|
||||
usingComponents: true,
|
||||
nvueStyleCompiler: 'uni-app',
|
||||
compilerVersion: 3,
|
||||
compatible: {
|
||||
ignoreVersion: true,
|
||||
},
|
||||
splashscreen: {
|
||||
alwaysShowBeforeRender: true,
|
||||
waiting: true,
|
||||
autoclose: true,
|
||||
delay: 0,
|
||||
},
|
||||
/* 模块配置 */
|
||||
modules: {
|
||||
Maps: {},
|
||||
Messaging: {},
|
||||
Contacts: {},
|
||||
Camera: {},
|
||||
},
|
||||
/* 应用发布信息 */
|
||||
distribute: {
|
||||
/* android打包配置 */
|
||||
android: {
|
||||
minSdkVersion: 30,
|
||||
targetSdkVersion: 30,
|
||||
abiFilters: ['armeabi-v7a', 'arm64-v8a'],
|
||||
permissions: [
|
||||
'<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>',
|
||||
'<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>',
|
||||
'<uses-permission android:name="android.permission.VIBRATE"/>',
|
||||
'<uses-permission android:name="android.permission.READ_LOGS"/>',
|
||||
'<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>',
|
||||
'<uses-feature android:name="android.hardware.camera.autofocus"/>',
|
||||
'<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>',
|
||||
'<uses-permission android:name="android.permission.CAMERA"/>',
|
||||
'<uses-permission android:name="android.permission.GET_ACCOUNTS"/>',
|
||||
'<uses-permission android:name="android.permission.READ_PHONE_STATE"/>',
|
||||
'<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>',
|
||||
'<uses-permission android:name="android.permission.WAKE_LOCK"/>',
|
||||
'<uses-permission android:name="android.permission.FLASHLIGHT"/>',
|
||||
'<uses-feature android:name="android.hardware.camera"/>',
|
||||
'<uses-permission android:name="android.permission.WRITE_SETTINGS"/>',
|
||||
],
|
||||
},
|
||||
/* ios打包配置 */
|
||||
ios: {},
|
||||
/* SDK配置 */
|
||||
sdkConfigs: {
|
||||
maps: {
|
||||
amap: {
|
||||
name: 'amap_15931993294Bqxlq8EgG',
|
||||
appkey_ios: 'c913e46ffdf548ebc56ac1cf4d883e7e',
|
||||
appkey_android: 'c913e46ffdf548ebc56ac1cf4d883e7e',
|
||||
},
|
||||
},
|
||||
},
|
||||
/* 图标配置 */
|
||||
icons: {
|
||||
android: {
|
||||
hdpi: 'src/static/app/icons/72x72.png',
|
||||
xhdpi: 'src/static/app/icons/96x96.png',
|
||||
xxhdpi: 'src/static/app/icons/144x144.png',
|
||||
xxxhdpi: 'src/static/app/icons/192x192.png',
|
||||
},
|
||||
ios: {
|
||||
appstore: 'src/static/app/icons/1024x1024.png',
|
||||
ipad: {
|
||||
app: 'src/static/app/icons/76x76.png',
|
||||
'app@2x': 'src/static/app/icons/152x152.png',
|
||||
notification: 'src/static/app/icons/20x20.png',
|
||||
'notification@2x': 'src/static/app/icons/40x40.png',
|
||||
'proapp@2x': 'src/static/app/icons/167x167.png',
|
||||
settings: 'src/static/app/icons/29x29.png',
|
||||
'settings@2x': 'src/static/app/icons/58x58.png',
|
||||
spotlight: 'src/static/app/icons/40x40.png',
|
||||
'spotlight@2x': 'src/static/app/icons/80x80.png',
|
||||
},
|
||||
iphone: {
|
||||
'app@2x': 'src/static/app/icons/120x120.png',
|
||||
'app@3x': 'src/static/app/icons/180x180.png',
|
||||
'notification@2x': 'src/static/app/icons/40x40.png',
|
||||
'notification@3x': 'src/static/app/icons/60x60.png',
|
||||
'settings@2x': 'src/static/app/icons/58x58.png',
|
||||
'settings@3x': 'src/static/app/icons/87x87.png',
|
||||
'spotlight@2x': 'src/static/app/icons/80x80.png',
|
||||
'spotlight@3x': 'src/static/app/icons/120x120.png',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
/* 快应用特有相关 */
|
||||
quickapp: {},
|
||||
/* 小程序特有相关 */
|
||||
'mp-weixin': {
|
||||
appid: VITE_WX_APPID,
|
||||
setting: {
|
||||
urlCheck: false,
|
||||
minified: true
|
||||
},
|
||||
usingComponents: true,
|
||||
// __usePrivacyCheck__: true,
|
||||
},
|
||||
'mp-alipay': {
|
||||
usingComponents: true,
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
'mp-baidu': {
|
||||
usingComponents: true,
|
||||
},
|
||||
'mp-toutiao': {
|
||||
usingComponents: true,
|
||||
},
|
||||
h5: {
|
||||
router: {
|
||||
base: VITE_APP_PUBLIC_BASE,
|
||||
},
|
||||
sdkConfigs: {
|
||||
maps: {
|
||||
amap: {
|
||||
key: '21f194a0d33197f874f7bbdd198419be',
|
||||
securityJsCode: 'a46b425f31a4de445b2966d998fba851',
|
||||
serviceHost: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'app-harmony': {
|
||||
distribute: {
|
||||
bundleName: 'uniapp.demo.test',
|
||||
},
|
||||
},
|
||||
uniStatistics: {
|
||||
enable: false,
|
||||
},
|
||||
vueVersion: '3',
|
||||
})
|
||||
109
mock/getChatList.ts
Normal file
109
mock/getChatList.ts
Normal file
@ -0,0 +1,109 @@
|
||||
// mock/getChatLists.js
|
||||
import Mock from 'mockjs'
|
||||
|
||||
export default [
|
||||
{
|
||||
url: '/api/eoa/im/newApi/getChatList',
|
||||
method: 'get', // 或 post
|
||||
response: () => {
|
||||
return Mock.mock({
|
||||
code: 200,
|
||||
success: true,
|
||||
result: {
|
||||
'logVoList|8-10': [
|
||||
{
|
||||
'id|+1': 1,
|
||||
fromUserName: '@cname',
|
||||
sendTime: Mock.mock('@date("yyyy-MM-dd")'),
|
||||
fromAvatar: () => {
|
||||
const sentences = [
|
||||
'https://picsum.photos/100/100',
|
||||
'https://random.imagecdn.app/100/100',
|
||||
'https://q1.qlogo.cn/g?b=qq&nk=190848757&s=100',
|
||||
'https://dummyimage.com/100x100/000/fff&text=%E6%9D%8E%E5%9B%9B',
|
||||
'https://dummyimage.com/100x100/f37b1d/fff&text=%E7%8E%8B%E4%BA%94',
|
||||
'https://dummyimage.com/100x100/59c7b8/fff&text=%E5%85%AD%E5%AD%90',
|
||||
]
|
||||
return sentences[Math.floor(Math.random() * sentences.length)]
|
||||
},
|
||||
'type|1': ['friend', 'discussion', 'group'],
|
||||
'izTop|1': [1, 0],
|
||||
'status|1': ['offline', 'online'],
|
||||
'msgFrom|+12': 4000,
|
||||
'msgTo|+34': 100,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/api/eoa/im/newApi/creatFriendSession',
|
||||
method: 'post', // 或 post
|
||||
response: () => {
|
||||
return Mock.mock({
|
||||
code: 200,
|
||||
success: true,
|
||||
result: {
|
||||
'accountId|+100': 4000,
|
||||
avatar: 'https://q1.qlogo.cn/g?b=qq&nk=190848757&s=100',
|
||||
email: '@email',
|
||||
'id|12': 112,
|
||||
'msgTo|+34': 100,
|
||||
'phone|9': 123,
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/api/eoa/im/newApi/records',
|
||||
method: 'get', // 或 post
|
||||
response: () => {
|
||||
return Mock.mock({
|
||||
code: 200,
|
||||
success: true,
|
||||
result: {
|
||||
'records|8-10': [
|
||||
{
|
||||
'id|+1': 1,
|
||||
fromUserName: '@cname',
|
||||
sendTime: Mock.mock('@date("yyyy-MM-dd")'),
|
||||
fromAvatar: 'https://dummyimage.com/100x100/000/fff&text=%E6%9D%8E%E5%9B%9B',
|
||||
'type|1': ['friend', 'discussion', 'group'],
|
||||
'izTop|1': [1, 0],
|
||||
'status|1': ['offline', 'online'],
|
||||
'msgFrom|+12': 4000,
|
||||
'msgTo|+34': 100,
|
||||
// msgData: Mock.mock('@cparagraph()'),
|
||||
msgData: () => Mock.mock('@cparagraph()'),
|
||||
userId: '1678948772039729154',
|
||||
msgType: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/api/eoa/im/newApi/creatFriendSession',
|
||||
method: 'post', // 或 post
|
||||
response: () => {
|
||||
return Mock.mock({
|
||||
code: 200,
|
||||
success: true,
|
||||
result: 'success',
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/api/eoa/im/newApi/sendMessage',
|
||||
method: 'post', // 或 post
|
||||
response: () => {
|
||||
return Mock.mock({
|
||||
code: 200,
|
||||
success: true,
|
||||
result: 'success',
|
||||
})
|
||||
},
|
||||
},
|
||||
]
|
||||
13
openapi-ts-request.config.ts
Normal file
13
openapi-ts-request.config.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import type { GenerateServiceProps } from 'openapi-ts-request'
|
||||
|
||||
export default [
|
||||
{
|
||||
schemaPath: 'http://petstore.swagger.io/v2/swagger.json',
|
||||
serversPath: './src/service/app',
|
||||
requestLibPath: `import request from '@/utils/request';\n import { CustomRequestOptions } from '@/interceptors/request';`,
|
||||
requestOptionsType: 'CustomRequestOptions',
|
||||
isGenReactQuery: true,
|
||||
reactQueryMode: 'vue',
|
||||
isGenJavaScript: false,
|
||||
},
|
||||
] as GenerateServiceProps[]
|
||||
156
package.json
Normal file
156
package.json
Normal file
@ -0,0 +1,156 @@
|
||||
{
|
||||
"name": "JeecgUniapp3",
|
||||
"type": "commonjs",
|
||||
"version": "3.0.0",
|
||||
"description": "JeecgBoot配套APP移动框架,一份代码多终端适配,支持小程序、H5、APP、ios、安卓、鸿蒙",
|
||||
"engines": {
|
||||
"node": ">=18",
|
||||
"pnpm": ">=7.30"
|
||||
},
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"uvm": "npx @dcloudio/uvm@latest",
|
||||
"uvm-rm": "node ./scripts/postupgrade.js",
|
||||
"postuvm": "echo upgrade uni-app success!",
|
||||
"dev:app": "uni -p app",
|
||||
"dev:app-android": "uni -p app-android",
|
||||
"dev:app-ios": "uni -p app-ios",
|
||||
"dev:custom": "uni -p",
|
||||
"dev": "uni",
|
||||
"dev:h5": "uni",
|
||||
"dev:h5:ssr": "uni --ssr",
|
||||
"dev:mp": "uni -p mp-weixin",
|
||||
"dev:mp-alipay": "uni -p mp-alipay",
|
||||
"dev:mp-baidu": "uni -p mp-baidu",
|
||||
"dev:mp-jd": "uni -p mp-jd",
|
||||
"dev:mp-kuaishou": "uni -p mp-kuaishou",
|
||||
"dev:mp-lark": "uni -p mp-lark",
|
||||
"dev:mp-qq": "uni -p mp-qq",
|
||||
"dev:mp-toutiao": "uni -p mp-toutiao",
|
||||
"dev:mp-weixin": "uni -p mp-weixin",
|
||||
"dev:mp-xhs": "uni -p mp-xhs",
|
||||
"dev:quickapp-webview": "uni -p quickapp-webview",
|
||||
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
|
||||
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
|
||||
"build:app": "uni build -p app",
|
||||
"build:app-android": "uni build -p app-android",
|
||||
"build:app-ios": "uni build -p app-ios",
|
||||
"build:custom": "uni build -p",
|
||||
"build:h5": "uni build",
|
||||
"build": "uni build",
|
||||
"build:h5:ssr": "uni build --ssr",
|
||||
"build:mp-alipay": "uni build -p mp-alipay",
|
||||
"build:mp": "uni build -p mp-weixin",
|
||||
"build:mp-baidu": "uni build -p mp-baidu",
|
||||
"build:mp-jd": "uni build -p mp-jd",
|
||||
"build:mp-kuaishou": "uni build -p mp-kuaishou",
|
||||
"build:mp-lark": "uni build -p mp-lark",
|
||||
"build:mp-qq": "uni build -p mp-qq",
|
||||
"build:mp-toutiao": "uni build -p mp-toutiao",
|
||||
"build:mp-weixin": "uni build -p mp-weixin",
|
||||
"build:mp-xhs": "uni build -p mp-xhs",
|
||||
"build:quickapp-webview": "uni build -p quickapp-webview",
|
||||
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
|
||||
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
|
||||
"type-check": "vue-tsc --noEmit",
|
||||
"release": "standard-version",
|
||||
"cz": "czg",
|
||||
"openapi-ts-request": "openapi-ts"
|
||||
},
|
||||
"resolutions": {
|
||||
"bin-wrapper": "npm:bin-wrapper-china"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dcloudio/uni-app": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-app-harmony": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-app-plus": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-components": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-h5": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-mp-alipay": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-mp-baidu": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-mp-jd": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-mp-kuaishou": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-mp-lark": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-mp-qq": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-mp-toutiao": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-mp-weixin": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-mp-xhs": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-quickapp-webview": "3.0.0-4030620241128001",
|
||||
"@tanstack/vue-query": "^5.62.16",
|
||||
"@vant/area-data": "^2.0.0",
|
||||
"abortcontroller-polyfill": "^1.7.8",
|
||||
"base-64": "^1.0.0",
|
||||
"dayjs": "1.11.10",
|
||||
"echarts": "^5.6.0",
|
||||
"md5": "^2.3.0",
|
||||
"pinia": "2.0.36",
|
||||
"pinia-plugin-persistedstate": "3.2.1",
|
||||
"qs": "6.5.3",
|
||||
"uni-parse-pages": "^0.0.1",
|
||||
"vue": "3.4.21",
|
||||
"vue-i18n": "9.1.9",
|
||||
"wot-design-uni": "^1.4.0",
|
||||
"z-paging": "^2.8.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^18.6.1",
|
||||
"@commitlint/config-conventional": "^18.6.3",
|
||||
"@dcloudio/types": "^3.4.14",
|
||||
"@dcloudio/uni-automator": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-cli-shared": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-stacktracey": "3.0.0-4030620241128001",
|
||||
"@dcloudio/uni-uts-v1": "3.0.0-4030620241128001",
|
||||
"@dcloudio/vite-plugin-uni": "3.0.0-4030620241128001",
|
||||
"@esbuild/darwin-arm64": "0.20.2",
|
||||
"@esbuild/darwin-x64": "0.20.2",
|
||||
"@iconify-json/carbon": "^1.2.4",
|
||||
"@rollup/rollup-darwin-x64": "^4.28.0",
|
||||
"@types/node": "^20.17.9",
|
||||
"@types/wechat-miniprogram": "^3.4.8",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"@uni-helper/uni-types": "1.0.0-alpha.3",
|
||||
"@uni-helper/vite-plugin-uni-layouts": "^0.1.10",
|
||||
"@uni-helper/vite-plugin-uni-manifest": "^0.2.7",
|
||||
"@uni-helper/vite-plugin-uni-pages": "0.2.20",
|
||||
"@uni-helper/vite-plugin-uni-platform": "^0.0.4",
|
||||
"@unocss/preset-legacy-compat": "^0.59.4",
|
||||
"@vue/runtime-core": "^3.4.21",
|
||||
"@vue/tsconfig": "^0.1.3",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"commitlint": "^18.6.1",
|
||||
"czg": "^1.9.4",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-import-resolver-typescript": "^3.7.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-vue": "^9.32.0",
|
||||
"mockjs": "^1.1.0",
|
||||
"openapi-ts-request": "^1.1.2",
|
||||
"postcss": "^8.4.49",
|
||||
"postcss-html": "^1.7.0",
|
||||
"postcss-scss": "^4.0.9",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"sass": "1.77.6",
|
||||
"standard-version": "^9.5.0",
|
||||
"stylelint": "^16.11.0",
|
||||
"stylelint-config-html": "^1.1.0",
|
||||
"stylelint-config-recess-order": "^4.6.0",
|
||||
"stylelint-config-recommended": "^14.0.1",
|
||||
"stylelint-config-recommended-scss": "^14.1.0",
|
||||
"stylelint-config-recommended-vue": "^1.5.0",
|
||||
"stylelint-prettier": "^5.0.2",
|
||||
"terser": "^5.36.0",
|
||||
"typescript": "^5.7.2",
|
||||
"unocss": "^0.58.9",
|
||||
"unocss-applet": "^0.7.8",
|
||||
"unplugin-auto-import": "^0.17.8",
|
||||
"vite": "5.2.8",
|
||||
"vite-plugin-mock": "^3.0.2",
|
||||
"vite-plugin-restart": "^0.4.2",
|
||||
"vitepress": "^1.5.0",
|
||||
"vue-tsc": "^1.0.24"
|
||||
}
|
||||
}
|
||||
55
pages.config.ts
Normal file
55
pages.config.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
|
||||
|
||||
export default defineUniPages({
|
||||
globalStyle: {
|
||||
navigationStyle: 'default',
|
||||
navigationBarTitleText: 'uniapp',
|
||||
navigationBarBackgroundColor: '#f8f8f8',
|
||||
navigationBarTextStyle: 'black',
|
||||
backgroundColor: '#FFFFFF',
|
||||
},
|
||||
easycom: {
|
||||
autoscan: true,
|
||||
custom: {
|
||||
'^wd-(.*)': 'wot-design-uni/components/wd-$1/wd-$1.vue',
|
||||
'^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)':
|
||||
'z-paging/components/z-paging$1/z-paging$1.vue',
|
||||
},
|
||||
},
|
||||
tabBar: {
|
||||
color: '#aaa',
|
||||
selectedColor: '#39b54a',
|
||||
backgroundColor: '#F8F8F8',
|
||||
borderStyle: 'black',
|
||||
height: '50px',
|
||||
fontSize: '11px',
|
||||
iconWidth: '24px',
|
||||
spacing: '3px',
|
||||
list: [
|
||||
{
|
||||
iconPath: 'static/tabbar/tabbar-message-2.png',
|
||||
selectedIconPath: 'static/tabbar/tabbar-message.png',
|
||||
pagePath: 'pages/message/message',
|
||||
text: '消息',
|
||||
},
|
||||
{
|
||||
iconPath: 'static/tabbar/tabbar-home-2.png',
|
||||
selectedIconPath: 'static/tabbar/tabbar-home.png',
|
||||
pagePath: 'pages/index/index',
|
||||
text: '协作',
|
||||
},
|
||||
{
|
||||
iconPath: 'static/tabbar/tabbar-workHome-2.png',
|
||||
selectedIconPath: 'static/tabbar/tabbar-workHome.png',
|
||||
pagePath: 'pages/workHome/index',
|
||||
text: '工作台',
|
||||
},
|
||||
{
|
||||
iconPath: 'static/tabbar/tabbar-user-2.png',
|
||||
selectedIconPath: 'static/tabbar/tabbar-user.png',
|
||||
pagePath: 'pages/user/people',
|
||||
text: '个人',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
15207
pnpm-lock.yaml
Normal file
15207
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
36
scripts/postupgrade.js
Normal file
36
scripts/postupgrade.js
Normal file
@ -0,0 +1,36 @@
|
||||
// # 执行 `pnpm upgrade` 后会升级 `uniapp` 相关依赖
|
||||
// # 在升级完后,会自动添加很多无用依赖,这需要删除以减小依赖包体积
|
||||
// # 只需要执行下面的命令即可
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { exec } = require('child_process')
|
||||
|
||||
// 定义要执行的命令
|
||||
const dependencies = [
|
||||
'@dcloudio/uni-app-harmony',
|
||||
// TODO: 如果需要某个平台的小程序,请手动删除或注释掉
|
||||
'@dcloudio/uni-mp-alipay',
|
||||
'@dcloudio/uni-mp-baidu',
|
||||
'@dcloudio/uni-mp-jd',
|
||||
'@dcloudio/uni-mp-kuaishou',
|
||||
'@dcloudio/uni-mp-lark',
|
||||
'@dcloudio/uni-mp-qq',
|
||||
'@dcloudio/uni-mp-toutiao',
|
||||
'@dcloudio/uni-mp-xhs',
|
||||
'@dcloudio/uni-quickapp-webview',
|
||||
// i18n模板要注释掉下面的
|
||||
'vue-i18n',
|
||||
]
|
||||
|
||||
// 使用exec执行命令
|
||||
exec(`pnpm un ${dependencies.join(' ')}`, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
// 如果有错误,打印错误信息
|
||||
console.error(`执行出错: ${error}`)
|
||||
return
|
||||
}
|
||||
// 打印正常输出
|
||||
console.log(`stdout: ${stdout}`)
|
||||
// 如果有错误输出,也打印出来
|
||||
console.error(`stderr: ${stderr}`)
|
||||
})
|
||||
118
src/App.vue
Normal file
118
src/App.vue
Normal file
@ -0,0 +1,118 @@
|
||||
<script lang="ts">
|
||||
import { onLaunch, onShow, onHide, onLoad, onReady } from '@dcloudio/uni-app'
|
||||
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
|
||||
import { beforEach } from '@/router/index'
|
||||
import { jurisdictionApi } from '@/api/system/login';
|
||||
import { useAppStore } from '@/store'
|
||||
|
||||
export default {
|
||||
onLaunch: function (options) {
|
||||
console.log('App Launch')
|
||||
console.log('应用启动路径:', options.path)
|
||||
},
|
||||
onShow: function (options) {
|
||||
console.log('App Show')
|
||||
console.log('应用启动路径:', options.path)
|
||||
// 首次进入页面时路由拦截
|
||||
setTimeout(() => {
|
||||
const currentPage = options.path
|
||||
beforEach({ path: '/' }, { path: currentPage, fullPath: currentPage }, (data) => {
|
||||
if (data?.path) {
|
||||
uni.redirectTo({ url: data.path })
|
||||
}
|
||||
})
|
||||
}, 100)
|
||||
|
||||
jurisdictionApi("1827997127165677570").then((res : any) => {
|
||||
// 0正常 1灰化
|
||||
if (res.success) {
|
||||
const appStore = useAppStore()
|
||||
appStore.setIsGray(res.result.value) // 更新状态
|
||||
}
|
||||
})
|
||||
|
||||
},
|
||||
onHide: function () {
|
||||
console.log('App Hide')
|
||||
},
|
||||
|
||||
// 全局变量
|
||||
globalData: {
|
||||
isLocalConfig: true,
|
||||
systemInfo: uni.getSystemInfoSync(),
|
||||
navHeight: 44,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.gray {
|
||||
filter: grayscale(1);
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
font-family:
|
||||
Helvetica Neue,
|
||||
Helvetica,
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
uni-page-body {
|
||||
height: 100%;
|
||||
|
||||
&>uni-view {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.shadow-warp {
|
||||
position: relative;
|
||||
box-shadow: 0 0 5px rgba(168, 92, 92, 0.1);
|
||||
}
|
||||
|
||||
/* stylelint-disable selector-type-no-unknown */
|
||||
button::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
swiper,
|
||||
scroll-view {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
// 单行省略,优先使用 unocss: text-ellipsis
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// 两行省略
|
||||
.ellipsis-2 {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
// 三行省略
|
||||
.ellipsis-3 {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
</style>
|
||||
37
src/api/system/login.ts
Normal file
37
src/api/system/login.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { http } from '@/utils/http'; // @/ 已经映射到 ./src/
|
||||
// ts对 HTTP 方法名称的大小写敏感。在 TypeScript 的类型定义中,HTTP 方法通常被定义为全大写的字符串字面量类型,如 "POST",而非"post"。
|
||||
interface LoginParams {
|
||||
username: string;
|
||||
password: string;
|
||||
captcha?: string; //非必填字段
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一的登录方法
|
||||
* @param config 登录参数
|
||||
* @returns 登录请求的 Promise
|
||||
*/
|
||||
export function loginApi(config : LoginParams) {
|
||||
// 如果传了 captcha,走本地登录(/sys/login),否则走单点登录(/sys/sinopecLogin)
|
||||
const url = config.captcha ? '/sys/login' : '/sys/sinopecLogin';
|
||||
return http({
|
||||
url,
|
||||
method: 'POST',
|
||||
data: config,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取是否灰化
|
||||
* @param id 登录参数
|
||||
* @returns 0正常 1灰化
|
||||
*/
|
||||
export function jurisdictionApi(id : string) { // 是否灰化
|
||||
return http({
|
||||
url: '/CxcJurisdiction/cxcJurisdiction/queryById',
|
||||
method: 'GET',
|
||||
data: {
|
||||
id
|
||||
}
|
||||
})
|
||||
}
|
||||
117
src/common/areaData/Area.ts
Normal file
117
src/common/areaData/Area.ts
Normal file
@ -0,0 +1,117 @@
|
||||
import {pcaa as REGION_DATA} from "./pcaUtils";
|
||||
|
||||
/**
|
||||
* Area 属性all的类型
|
||||
*/
|
||||
interface PlainPca {
|
||||
id: string;
|
||||
text: string;
|
||||
pid: string;
|
||||
index: Number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 省市区工具类 -解决列表省市区组件的翻译问题
|
||||
*/
|
||||
class Area {
|
||||
all: PlainPca[];
|
||||
|
||||
/**
|
||||
* 构造器
|
||||
* @param pcaa
|
||||
*/
|
||||
constructor(pcaa?) {
|
||||
if (!pcaa) {
|
||||
pcaa = REGION_DATA;
|
||||
}
|
||||
let arr: PlainPca[] = [];
|
||||
const province = pcaa['86'];
|
||||
Object.keys(province).map((key) => {
|
||||
arr.push({ id: key, text: province[key], pid: '86', index: 1 });
|
||||
const city = pcaa[key];
|
||||
Object.keys(city).map((key2) => {
|
||||
arr.push({ id: key2, text: city[key2], pid: key, index: 2 });
|
||||
const qu = pcaa[key2];
|
||||
if (qu) {
|
||||
Object.keys(qu).map((key3) => {
|
||||
arr.push({ id: key3, text: qu[key3], pid: key2, index: 3 });
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
this.all = arr;
|
||||
}
|
||||
|
||||
get pca() {
|
||||
return this.all;
|
||||
}
|
||||
|
||||
getCode(text) {
|
||||
if (!text || text.length == 0) {
|
||||
return '';
|
||||
}
|
||||
for (let item of this.all) {
|
||||
if (item.text === text) {
|
||||
return item.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//update-begin-author:liusq---date:20230404--for: [issue/382]省市区组件JAreaLinkage数据不回显---
|
||||
getText(code,index=3) {
|
||||
if (!code || code.length == 0) {
|
||||
return '';
|
||||
}
|
||||
let arr = [];
|
||||
this.getAreaBycode(code, arr, index);
|
||||
return arr.join('/');
|
||||
}
|
||||
//update-end-author:liusq---date:20230404--for: [issue/382]省市区组件JAreaLinkage数据不回显---
|
||||
|
||||
getRealCode(code) {
|
||||
let arr = [];
|
||||
this.getPcode(code, arr, 3);
|
||||
return arr;
|
||||
}
|
||||
|
||||
getPcode(id, arr, index) {
|
||||
for (let item of this.all) {
|
||||
if (item.id === id && item.index == index) {
|
||||
arr.unshift(id);
|
||||
if (item.pid != '86') {
|
||||
this.getPcode(item.pid, arr, --index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAreaBycode(code, arr, index) {
|
||||
for (let item of this.all) {
|
||||
if (item.id === code && item.index == index) {
|
||||
arr.unshift(item.text);
|
||||
if (item.pid != '86') {
|
||||
this.getAreaBycode(item.pid, arr, --index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const jeecgAreaData = new Area();
|
||||
|
||||
// 根据code找文本
|
||||
const getAreaTextByCode = function (code) {
|
||||
let index = 3;
|
||||
//update-begin-author:liusq---date:20220531--for: 判断code是否是多code逗号分割的字符串,是的话,获取最后一位的code ---
|
||||
if (code && code.includes(',')) {
|
||||
index = code.split(",").length;
|
||||
code = code.substr(code.lastIndexOf(',') + 1);
|
||||
}
|
||||
//update-end-author:liusq---date:20220531--for: 判断code是否是多code逗号分割的字符串,是的话,获取最后一位的code ---
|
||||
return jeecgAreaData.getText(code,index);
|
||||
};
|
||||
// 根据code找文本
|
||||
const getAreaArrByCode = function (code) {
|
||||
return jeecgAreaData.getRealCode(code);
|
||||
};
|
||||
|
||||
export { getAreaTextByCode,getAreaArrByCode };
|
||||
57
src/common/areaData/pcaUtils.ts
Normal file
57
src/common/areaData/pcaUtils.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import {areaList} from '@vant/area-data'
|
||||
|
||||
// 扁平化的省市区数据
|
||||
export const pcaa = freezeDeep(usePlatPcaaData())
|
||||
|
||||
/**
|
||||
* 获取扁平化的省市区数据
|
||||
*/
|
||||
function usePlatPcaaData() {
|
||||
const {city_list: city, county_list: county, province_list: province} = areaList;
|
||||
const dataMap = new Map<string, Recordable>()
|
||||
const flatData: Recordable = {'86': province}
|
||||
// 省
|
||||
Object.keys(province).forEach((code) => {
|
||||
flatData[code] = {}
|
||||
dataMap.set(code.slice(0, 2), flatData[code])
|
||||
})
|
||||
// 市区
|
||||
Object.keys(city).forEach((code) => {
|
||||
flatData[code] = {}
|
||||
dataMap.set(code.slice(0, 4), flatData[code])
|
||||
// 填充上一级
|
||||
const getProvince = dataMap.get(code.slice(0, 2))
|
||||
if (getProvince) {
|
||||
getProvince[code] = city[code]
|
||||
}
|
||||
});
|
||||
// 县
|
||||
Object.keys(county).forEach((code) => {
|
||||
// 填充上一级
|
||||
const getCity = dataMap.get(code.slice(0, 4))
|
||||
if (getCity) {
|
||||
getCity[code] = county[code]
|
||||
}
|
||||
});
|
||||
return flatData
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* 深度冻结对象
|
||||
* @param obj Object or Array
|
||||
*/
|
||||
export function freezeDeep(obj: Recordable | Recordable[]) {
|
||||
if (obj != null) {
|
||||
if (Array.isArray(obj)) {
|
||||
obj.forEach(item => freezeDeep(item))
|
||||
} else if (typeof obj === 'object') {
|
||||
Object.values(obj).forEach(value => {
|
||||
freezeDeep(value)
|
||||
})
|
||||
}
|
||||
Object.freeze(obj)
|
||||
}
|
||||
return obj
|
||||
}
|
||||
135
src/common/constants.ts
Normal file
135
src/common/constants.ts
Normal file
@ -0,0 +1,135 @@
|
||||
export const ACCESS_TOKEN = 'Access-Token'
|
||||
export const USER_NAME = 'login_username'
|
||||
export const USER_INFO = 'login_user_info'
|
||||
export const NAV_BAR_COLOR = 'bg-gradual-blue'
|
||||
export const APP_ROUTE = 'app_route_list'
|
||||
export const APP_CONFIG = 'app_config'
|
||||
export const X_TENANT_ID = 'X-Tenant-Id'
|
||||
export const X_Low_App_ID = 'X-Low-App-ID'
|
||||
export const TENANT_LIST = 'tenant_list'
|
||||
export const ROUTE_PARAMS = "cacheRouteParams"
|
||||
export const HOME_PAGE = "/pages/message/message"
|
||||
//首页配置项缓存时间10分钟
|
||||
export const HOME_CONFIG_EXPIRED_TIME = 10*60
|
||||
export const phone = '---'
|
||||
export const email = '---'
|
||||
export const company = '---'
|
||||
|
||||
|
||||
const STORAGE_OPTIONS = {
|
||||
namespace: 'pro__', // key prefix
|
||||
name: 'ls', // name variable Vue.[ls] or this.[$ls],
|
||||
storage: 'local', // storage name session, local, memory
|
||||
}
|
||||
|
||||
export default STORAGE_OPTIONS;
|
||||
//类型条件
|
||||
export const conditionObj = {
|
||||
input:[{label:"包含",value:"like"},{label:"以...开始",value:"right_like"},{label:"以...结尾",value:"left_like"},{label:"在...中",value:"in"}],
|
||||
number:[{label:"大于",value:"gt"},{label:"大于等于",value:"ge"},{label:"小于",value:"lt"},{label:"小于等于",value:"le"}],
|
||||
date:[{label:"大于",value:"gt"},{label:"大于等于",value:"ge"},{label:"小于",value:"lt"},{label:"小于等于",value:"le"}],
|
||||
select:[],
|
||||
checkbox:[{label:"多词匹配",value:"elemMatch"}],
|
||||
}
|
||||
/**
|
||||
* 颜色板
|
||||
* classic:经典
|
||||
* technology:科技
|
||||
* business:商务
|
||||
* botany:植物
|
||||
* natural:自然
|
||||
* colour:彩色
|
||||
*/
|
||||
export const colorPanel = {
|
||||
classic:["#64b5f6","#4db6ac","#ffb74d","#e57373","#9575cd","#a1887f","#90a4ae","#4dd0e1","#81c784","#ff8a65"],
|
||||
technology:["#3a5b84","#4d6e98","#7594b9","#bfd7f2","#18619f","#408aca","#5ea8e9","#81c3fc","#71a5cb","#a1cae4"],
|
||||
business:["#ccedf7","#b9dcf0","#12a0e7","#0663a4","#458890","#97d9cd","#4bb8bf","#20899c","#f44336 ","#a2c7d9"],
|
||||
botany:["#34b392","#4ac2a6","#8ed1c0","#ccdec6","#61bdb5","#7993a1","#93a889","#5e8d83","#115040","#bcc5b4"],
|
||||
natural:["#85cacd","#a7d676","#fee159","#fbc78e","#ef918b","#a9b5ff","#e7daca","#fc803a","#fea1ac","#c2a3cd"],
|
||||
colour:["#fddb9c","#f9ae91","#f59193","#d47f97","#bd86a6","#f595a1","#624772","#fe7156","#ffbda3","#877fa8"]
|
||||
};
|
||||
//所有条件
|
||||
export const allCondition = [
|
||||
{label:"包含",value:"like"},
|
||||
{label:"以...开始",value:"right_like"},
|
||||
{label:"以...结尾",value:"left_like"},
|
||||
{label:"在...中",value:"in"},
|
||||
{label:"大于",value:"gt"},
|
||||
{label:"大于等于",value:"ge"},
|
||||
{label:"小于",value:"lt"},
|
||||
{label:"小于等于",value:"le"},
|
||||
{label:"多词匹配",value:"elemMatch"},
|
||||
{label:"等于",value:"eq"},
|
||||
{label:"不等于",value:"ne"},
|
||||
{label:"为空",value:"empty"},
|
||||
{label:"不为空",value:"not_empty"}
|
||||
]
|
||||
//仪表盘组件
|
||||
export const compList = [
|
||||
"JBar",
|
||||
"JStackBar",
|
||||
"JMultipleBar",
|
||||
"JNegativeBar",
|
||||
|
||||
"JLine",
|
||||
"JMultipleLine",
|
||||
"DoubleLineBar",
|
||||
|
||||
"JPie",
|
||||
"JRing",
|
||||
|
||||
"JFunnel",
|
||||
"JPyramidFunnel",
|
||||
|
||||
"JRadar",
|
||||
"JCircleRadar",
|
||||
|
||||
"JGauge",
|
||||
"JColorGauge",
|
||||
|
||||
"JScatter",
|
||||
"JBubble",
|
||||
|
||||
"JDragEditor",
|
||||
"JCarousel",
|
||||
"JIframe",
|
||||
"JNumber",
|
||||
"JCustomButton",
|
||||
"JPivotTable",
|
||||
|
||||
"JBubbleMap",
|
||||
"JBarMap",
|
||||
"JHeatMap",
|
||||
];
|
||||
//不包含操作的组件
|
||||
export const noActionList = [
|
||||
"JCustomButton",
|
||||
"JIframe",
|
||||
"JCarousel",
|
||||
"JDragEditor",
|
||||
];
|
||||
|
||||
//系统字段
|
||||
export const systemFields = [{
|
||||
dataIndex:"create_time",
|
||||
key:"create_time",
|
||||
title:"创建时间",
|
||||
type:'date',
|
||||
width:200
|
||||
},{
|
||||
dataIndex:"create_by",
|
||||
key:"create_by",
|
||||
title:"创建人",
|
||||
width:150
|
||||
},{
|
||||
dataIndex:"update_time",
|
||||
key:"update_time",
|
||||
title:"修改时间",
|
||||
type:'date',
|
||||
width:200
|
||||
},{
|
||||
dataIndex:"update_by",
|
||||
key:"update_by",
|
||||
title:"修改人",
|
||||
width:150
|
||||
}]
|
||||
108
src/common/is.ts
Normal file
108
src/common/is.ts
Normal file
@ -0,0 +1,108 @@
|
||||
const toString = Object.prototype.toString
|
||||
|
||||
export function is(val: unknown, type: string) {
|
||||
return toString.call(val) === `[object ${type}]`
|
||||
}
|
||||
|
||||
export function isDef<T = unknown>(val?: T): val is T {
|
||||
return typeof val !== 'undefined'
|
||||
}
|
||||
|
||||
export function isUnDef<T = unknown>(val?: T): val is T {
|
||||
return !isDef(val)
|
||||
}
|
||||
|
||||
export function isObject(val: any): val is Record<any, any> {
|
||||
return val !== null && is(val, 'Object')
|
||||
}
|
||||
|
||||
export function isEmpty<T = unknown>(val: T): val is T {
|
||||
if (isArray(val) || isString(val)) {
|
||||
return val.length === 0
|
||||
}
|
||||
|
||||
if (val instanceof Map || val instanceof Set) {
|
||||
return val.size === 0
|
||||
}
|
||||
|
||||
if (isObject(val)) {
|
||||
return Object.keys(val).length === 0
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function isDate(val: unknown): val is Date {
|
||||
return is(val, 'Date')
|
||||
}
|
||||
|
||||
export function isNull(val: unknown): val is null {
|
||||
return val === null
|
||||
}
|
||||
|
||||
export function isNullAndUnDef(val: unknown): val is null | undefined {
|
||||
return isUnDef(val) && isNull(val)
|
||||
}
|
||||
|
||||
export function isNullOrUnDef(val: unknown): val is null | undefined {
|
||||
return isUnDef(val) || isNull(val)
|
||||
}
|
||||
|
||||
export function isNumber(val: unknown): val is number {
|
||||
return is(val, 'Number')
|
||||
}
|
||||
|
||||
export function isPromise<T = any>(val: any): val is Promise<T> {
|
||||
// update-begin--author:sunjianlei---date:20211022---for: 不能既是 Promise 又是 Object --------
|
||||
return is(val, 'Promise') && isFunction(val.then) && isFunction(val.catch)
|
||||
// update-end--author:sunjianlei---date:20211022---for: 不能既是 Promise 又是 Object --------
|
||||
}
|
||||
|
||||
export function isString(val: unknown): val is string {
|
||||
return is(val, 'String')
|
||||
}
|
||||
|
||||
export function isJsonObjectString(val: string): val is string {
|
||||
if (!val) {
|
||||
return false
|
||||
}
|
||||
return val.startsWith('{') && val.endsWith('}')
|
||||
}
|
||||
|
||||
export function isFunction(val: unknown): val is Function {
|
||||
return typeof val === 'function'
|
||||
}
|
||||
|
||||
export function isBoolean(val: unknown): val is boolean {
|
||||
return is(val, 'Boolean')
|
||||
}
|
||||
|
||||
export function isRegExp(val: unknown): val is RegExp {
|
||||
return is(val, 'RegExp')
|
||||
}
|
||||
|
||||
export function isArray(val: any): val is Array<any> {
|
||||
return val && Array.isArray(val)
|
||||
}
|
||||
|
||||
export function isWindow(val: any): val is Window {
|
||||
return typeof window !== 'undefined' && is(val, 'Window')
|
||||
}
|
||||
|
||||
export function isElement(val: unknown): val is Element {
|
||||
return isObject(val) && !!val.tagName
|
||||
}
|
||||
|
||||
export function isMap(val: unknown): val is Map<any, any> {
|
||||
return is(val, 'Map')
|
||||
}
|
||||
|
||||
export const isServer = typeof window === 'undefined'
|
||||
|
||||
export const isClient = !isServer
|
||||
|
||||
export function isUrl(path: string): boolean {
|
||||
const reg =
|
||||
/(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/
|
||||
return reg.test(path)
|
||||
}
|
||||
140
src/common/socket.ts
Normal file
140
src/common/socket.ts
Normal file
@ -0,0 +1,140 @@
|
||||
// @ts-nocheck
|
||||
import { randomString } from './uitls'
|
||||
import { useUserStore } from '@/store/user'
|
||||
|
||||
const baseUrl = import.meta.env.VITE_SERVER_BASEURL
|
||||
|
||||
class socket {
|
||||
constructor() {
|
||||
this.socketUrl = baseUrl
|
||||
this.socketStart = false
|
||||
this.socketType = ''
|
||||
this.monitorSocketError()
|
||||
this.monitorSocketClose()
|
||||
this.socketReceive()
|
||||
}
|
||||
init(socket_type, callback?) {
|
||||
const userStore = useUserStore()
|
||||
const _this = this
|
||||
if (baseUrl) {
|
||||
if (this.socketStart) {
|
||||
console.log('webSocket已经启动了')
|
||||
} else {
|
||||
_this.socketType = socket_type
|
||||
let url =
|
||||
this.socketUrl.replace('https://', 'wss://').replace('http://', 'ws://') +
|
||||
'/' +
|
||||
socket_type +
|
||||
'/' +
|
||||
userStore.userInfo.userid +
|
||||
'_app'
|
||||
if (socket_type == 'eoaNewChatSocket') {
|
||||
let randomMessageId = randomString(6)
|
||||
url =
|
||||
this.socketUrl.replace('https://', 'wss://').replace('http://', 'ws://') +
|
||||
'/eoaNewChatSocket/' +
|
||||
userStore.userInfo.userid +
|
||||
'/' +
|
||||
randomMessageId
|
||||
}
|
||||
console.log('启动this.socketUrl连接地址:', url)
|
||||
// update-begin-author:taoyan date:20220422 for:v2.4.6 的 websocket 服务端,存在性能和安全问题。 #3278
|
||||
|
||||
let token = userStore.userInfo.token
|
||||
uni.connectSocket({
|
||||
url: url,
|
||||
method: 'GET',
|
||||
protocols: [token],
|
||||
})
|
||||
// update-end-author:taoyan date:20220422 for: v2.4.6 的 websocket 服务端,存在性能和安全问题。 #3278
|
||||
uni.onSocketOpen((res) => {
|
||||
this.socketStart = true
|
||||
callback && callback()
|
||||
console.log('WebSocket连接已打开!')
|
||||
})
|
||||
/* setTimeout(() => {
|
||||
_this.getHeartbeat();
|
||||
}, 5000); */
|
||||
}
|
||||
} else {
|
||||
console.log('config/baseUrl socketUrl为空')
|
||||
}
|
||||
}
|
||||
// Socket给服务器发送消息
|
||||
send(data, callback) {
|
||||
const userStore = useUserStore()
|
||||
const _this = this
|
||||
if (userStore.userInfo.userid) {
|
||||
data.userUid = userStore.userInfo.userid
|
||||
}
|
||||
console.log(data)
|
||||
uni.sendSocketMessage({
|
||||
data: JSON.stringify(data),
|
||||
success: () => {
|
||||
callback && callback(true)
|
||||
},
|
||||
fail: () => {
|
||||
callback && callback(false)
|
||||
},
|
||||
})
|
||||
}
|
||||
// Socket接收服务器发送过来的消息
|
||||
socketReceive() {
|
||||
const _this = this
|
||||
uni.onSocketMessage(function (res) {
|
||||
console.log('APP:--》收到服务器内容:')
|
||||
let data = JSON.parse(res.data)
|
||||
// console.log('收到服务器内容:', data);
|
||||
_this.acceptMessage && _this.acceptMessage(data)
|
||||
})
|
||||
}
|
||||
// 关闭Socket
|
||||
closeSocket() {
|
||||
const _this = this
|
||||
uni.closeSocket()
|
||||
_this.socketStart = false
|
||||
}
|
||||
// 监听Socket关闭
|
||||
monitorSocketClose() {
|
||||
const _this = this
|
||||
uni.onSocketClose(function (res) {
|
||||
console.log('WebSocket 已关闭!')
|
||||
_this.socketStart = false
|
||||
setTimeout(function () {
|
||||
_this.init(_this.socketType)
|
||||
}, 3000)
|
||||
})
|
||||
}
|
||||
// 监听Socket错误
|
||||
monitorSocketError() {
|
||||
const _this = this
|
||||
uni.onSocketError(function (res) {
|
||||
_this.socketStart = false
|
||||
console.log('WebSocket连接打开失败,请检查!')
|
||||
})
|
||||
}
|
||||
// 心跳
|
||||
getHeartbeat() {
|
||||
const userStore = useUserStore()
|
||||
const _this = this
|
||||
this.send(
|
||||
{
|
||||
type: '心跳',
|
||||
userUid: userStore.userInfo.userid,
|
||||
},
|
||||
(val) => {
|
||||
setTimeout(() => {
|
||||
if (val) {
|
||||
// _this.getHeartbeat();
|
||||
} else {
|
||||
if (!_this.socketStart) {
|
||||
// _this.init();
|
||||
}
|
||||
}
|
||||
}, 10000)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
const mySocket = new socket()
|
||||
export default mySocket
|
||||
331
src/common/uitls.ts
Normal file
331
src/common/uitls.ts
Normal file
@ -0,0 +1,331 @@
|
||||
import pagesJson from '../pages.json'
|
||||
// 引入uni-parse-pages
|
||||
import pagesJsonToRoutes from 'uni-parse-pages'
|
||||
import { colorPanel } from './constants'
|
||||
|
||||
/**
|
||||
* 缓存,默认有效期2小时
|
||||
* @param key 缓存key
|
||||
* @param value 缓存值
|
||||
* @param seconds 缓存时间(秒)
|
||||
* @returns {*}
|
||||
*/
|
||||
export function cache(key, value = null, seconds = 2 * 3600) {
|
||||
var timestamp = +new Date() / 1000
|
||||
if (key && value === null) {
|
||||
//获取缓存
|
||||
var val = uni.getStorageSync(key)
|
||||
if (val && val.length > 0) {
|
||||
var tmp = val.split('|')
|
||||
if (!tmp[2] || timestamp >= tmp[2]) {
|
||||
console.log('key已失效')
|
||||
//删除缓存
|
||||
uni.removeStorageSync(key)
|
||||
return ''
|
||||
} else {
|
||||
console.log('key未失效')
|
||||
if (tmp[1] == 'json') {
|
||||
return JSON.parse(tmp[0])
|
||||
}
|
||||
return tmp[0]
|
||||
}
|
||||
}
|
||||
} else if (key && value) {
|
||||
//设置缓存
|
||||
var expire = timestamp + seconds
|
||||
console.log('typeof value', typeof value)
|
||||
if (typeof value == 'object') {
|
||||
value = JSON.stringify(value) + '|json|' + expire
|
||||
} else {
|
||||
value = value + '|string|' + expire
|
||||
}
|
||||
uni.setStorageSync(key, value)
|
||||
} else {
|
||||
console.log('key不能空')
|
||||
}
|
||||
}
|
||||
|
||||
// 获取静态文件地址
|
||||
export const getStaticDomainURL = () => {
|
||||
return import.meta.env.VITE_SERVER_BASEURL + '/sys/common/static'
|
||||
}
|
||||
|
||||
export const getFileAccessHttpUrl = function (avatar, subStr?) {
|
||||
if (!avatar) return ''
|
||||
if (!subStr) subStr = 'http'
|
||||
if (avatar) {
|
||||
avatar = avatar.replace(/user_imgs\\/, 'user_imgs/')
|
||||
}
|
||||
if (avatar && avatar.startsWith(subStr)) {
|
||||
return avatar
|
||||
} else {
|
||||
return getStaticDomainURL() + '/' + avatar
|
||||
}
|
||||
}
|
||||
interface hasRouteType {
|
||||
name?: string
|
||||
path?: string
|
||||
routeList?: any
|
||||
}
|
||||
// 判断路由是否存在
|
||||
export const hasRoute = ({ name, path, routeList }: hasRouteType) => {
|
||||
routeList = routeList ?? pagesJsonToRoutes(pagesJson)
|
||||
if (path) {
|
||||
return !!routeList.find((item) => item.path === path)
|
||||
}
|
||||
if (name) {
|
||||
return !!routeList.find((item) => item.path.split('/').pop() === name)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 人性化显示时间
|
||||
*
|
||||
* @param {Object} datetime
|
||||
*/
|
||||
export function beautifyTime(datetime = '') {
|
||||
if (datetime == null) {
|
||||
return ''
|
||||
}
|
||||
datetime = datetime.toString().replace(/-/g, '/')
|
||||
let time = new Date()
|
||||
let outTime = new Date(datetime)
|
||||
if (/^[1-9]\d*$/.test(datetime)) {
|
||||
outTime = new Date(parseInt(datetime))
|
||||
}
|
||||
|
||||
if (time.getTime() < outTime.getTime()) {
|
||||
return parseTime(outTime, '{y}/{m}/{d}')
|
||||
}
|
||||
|
||||
if (time.getFullYear() != outTime.getFullYear()) {
|
||||
return parseTime(outTime, '{y}/{m}/{d}')
|
||||
}
|
||||
|
||||
if (time.getMonth() != outTime.getMonth()) {
|
||||
return parseTime(outTime, '{m}/{d}')
|
||||
}
|
||||
|
||||
if (time.getDate() != outTime.getDate()) {
|
||||
let day = outTime.getDate() - time.getDate()
|
||||
if (day == -1) {
|
||||
return parseTime(outTime, '昨天 {h}:{i}')
|
||||
}
|
||||
|
||||
if (day == -2) {
|
||||
return parseTime(outTime, '前天 {h}:{i}')
|
||||
}
|
||||
|
||||
return parseTime(outTime, '{m}-{d}')
|
||||
}
|
||||
|
||||
if (time.getHours() != outTime.getHours()) {
|
||||
return parseTime(outTime, '{h}:{i}')
|
||||
}
|
||||
|
||||
let minutes = outTime.getMinutes() - time.getMinutes()
|
||||
if (minutes == 0) {
|
||||
return '刚刚'
|
||||
}
|
||||
|
||||
minutes = Math.abs(minutes)
|
||||
return `${minutes}分钟前`
|
||||
}
|
||||
/**
|
||||
* 格式化时间
|
||||
* @param {Object} time
|
||||
* @param {Object} cFormat
|
||||
*/
|
||||
export function parseTime(time, cFormat) {
|
||||
if (arguments.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
let date
|
||||
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
|
||||
|
||||
if (typeof time === 'object') {
|
||||
date = time
|
||||
} else {
|
||||
if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
|
||||
time = parseInt(time)
|
||||
} else {
|
||||
time = new Date(time)
|
||||
}
|
||||
date = new Date(time.toString().replace(/-/g, '/'))
|
||||
}
|
||||
|
||||
const formatObj = {
|
||||
y: date.getFullYear(),
|
||||
m: date.getMonth() + 1,
|
||||
d: date.getDate(),
|
||||
h: date.getHours(),
|
||||
i: date.getMinutes(),
|
||||
s: date.getSeconds(),
|
||||
a: date.getDay(),
|
||||
}
|
||||
|
||||
const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
|
||||
const value = formatObj[key]
|
||||
// Note: getDay() returns 0 on Sunday
|
||||
if (key === 'a') {
|
||||
return ['日', '一', '二', '三', '四', '五', '六'][value]
|
||||
}
|
||||
|
||||
return value.toString().padStart(2, '0')
|
||||
})
|
||||
|
||||
return time_str
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机生成字符串
|
||||
* @param length 字符串的长度
|
||||
* @param chats 可选字符串区间(只会生成传入的字符串中的字符)
|
||||
* @return string 生成的字符串
|
||||
*/
|
||||
export function randomString(length, chats) {
|
||||
if (!length) length = 1
|
||||
if (!chats) chats = '0123456789qwertyuioplkjhgfdsazxcvbnm'
|
||||
let str = ''
|
||||
for (let i = 0; i < length; i++) {
|
||||
//@ts-ignore
|
||||
let num = randomNumber(0, chats.length - 1)
|
||||
str += chats[num]
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机生成数字
|
||||
*
|
||||
* 示例:生成长度为 12 的随机数:randomNumber(12)
|
||||
* 示例:生成 3~23 之间的随机数:randomNumber(3, 23)
|
||||
*
|
||||
* @param1 最小值 | 长度
|
||||
* @param2 最大值
|
||||
* @return int 生成后的数字
|
||||
*/
|
||||
export function randomNumber() {
|
||||
// 生成 最小值 到 最大值 区间的随机数
|
||||
const random = (min, max) => {
|
||||
return Math.floor(Math.random() * (max - min + 1) + min)
|
||||
}
|
||||
if (arguments.length === 1) {
|
||||
//@ts-ignore
|
||||
let [length] = arguments
|
||||
// 生成指定长度的随机数字,首位一定不是 0
|
||||
//@ts-ignore
|
||||
let nums = [...Array(length).keys()].map((i) => (i > 0 ? random(0, 9) : random(1, 9)))
|
||||
return parseInt(nums.join(''))
|
||||
} else if (arguments.length >= 2) {
|
||||
//@ts-ignore
|
||||
let [min, max] = arguments
|
||||
return random(min, max)
|
||||
} else {
|
||||
return Number.NaN
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间格式化
|
||||
* @param value
|
||||
* @param fmt
|
||||
* @returns {*}
|
||||
*/
|
||||
export function formatDate(value, fmt) {
|
||||
var regPos = /^\d+(\.\d+)?$/
|
||||
if (regPos.test(value)) {
|
||||
//如果是数字
|
||||
let getDate = new Date(value)
|
||||
let o = {
|
||||
'M+': getDate.getMonth() + 1,
|
||||
'd+': getDate.getDate(),
|
||||
'h+': getDate.getHours(),
|
||||
'H+': getDate.getHours(),
|
||||
'm+': getDate.getMinutes(),
|
||||
's+': getDate.getSeconds(),
|
||||
'q+': Math.floor((getDate.getMonth() + 3) / 3),
|
||||
S: getDate.getMilliseconds(),
|
||||
}
|
||||
if (/(y+)/.test(fmt)) {
|
||||
fmt = fmt.replace(RegExp.$1, (getDate.getFullYear() + '').substr(4 - RegExp.$1.length))
|
||||
}
|
||||
for (let k in o) {
|
||||
if (new RegExp('(' + k + ')').test(fmt)) {
|
||||
fmt = fmt.replace(
|
||||
RegExp.$1,
|
||||
RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length),
|
||||
)
|
||||
}
|
||||
}
|
||||
return fmt
|
||||
} else {
|
||||
//TODO
|
||||
if (value && value.length > 0) {
|
||||
value = value.trim()
|
||||
return value.substr(0, fmt.length)
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
// 通过时间或者时间戳获取对应antd的年、月、周、季度。
|
||||
export function getWeekMonthQuarterYear(date) {
|
||||
// 获取 ISO 周数的函数
|
||||
const getISOWeek = (date) => {
|
||||
const jan4 = new Date(date.getFullYear(), 0, 4)
|
||||
const oneDay = 86400000 // 一天的毫秒数
|
||||
return Math.ceil(((date - jan4.getTime()) / oneDay + jan4.getDay() + 1) / 7)
|
||||
}
|
||||
// 将时间戳转换为日期对象
|
||||
const dateObj = new Date(date)
|
||||
// 计算周
|
||||
const week = getISOWeek(dateObj)
|
||||
// 计算月
|
||||
const month = dateObj.getMonth() + 1 // 月份是从0开始的,所以要加1
|
||||
// 计算季度
|
||||
const quarter = Math.floor(dateObj.getMonth() / 3) + 1
|
||||
// 计算年
|
||||
const year = dateObj.getFullYear()
|
||||
return {
|
||||
year: `${year}`,
|
||||
month: `${year}-${month.toString().padStart(2, '0')}`,
|
||||
week: `${year}-${week}周`,
|
||||
quarter: `${year}-Q${quarter}`,
|
||||
}
|
||||
}
|
||||
|
||||
// 生成 1 到 10 之间的随机整数
|
||||
export function getRandomIntBetweenOneAndTen() {
|
||||
return Math.floor(Math.random() * 10) + 1;
|
||||
}
|
||||
/**
|
||||
* 获取随机颜色
|
||||
* @param {any} color
|
||||
* 颜色板
|
||||
* classic:经典
|
||||
* technology:科技
|
||||
* business:商务
|
||||
* botany:植物
|
||||
* natural:自然
|
||||
* colour:彩色
|
||||
* @return
|
||||
*/
|
||||
export function getRandomColor() {
|
||||
let colorType = ['classic','technology','business','botany','natural','colour'];
|
||||
// 生成一个随机索引,范围是从 0 到数组长度减 1
|
||||
let randomIndex = Math.floor(Math.random() * colorType.length);
|
||||
// 根据随机索引从数组中获取一个随机类型
|
||||
let randomColorType = colorType[randomIndex];
|
||||
return colorPanel['natural'][getRandomIntBetweenOneAndTen()] || '#00bcd4';
|
||||
}
|
||||
|
||||
// 消除后缀:
|
||||
export const getPlaceholder = (attrs: any = {}) => {
|
||||
let label = attrs.label
|
||||
if (label.endsWith(':') || label.endsWith(':')) {
|
||||
label = label.substr(0, label.length - 1)
|
||||
}
|
||||
return `请选择${label}`
|
||||
}
|
||||
135
src/common/work.ts
Normal file
135
src/common/work.ts
Normal file
@ -0,0 +1,135 @@
|
||||
/**
|
||||
* 常用服务
|
||||
* useful server
|
||||
*/
|
||||
|
||||
const icon_prefix = '/static/index/128/'
|
||||
|
||||
/*
|
||||
*/
|
||||
export const us = {
|
||||
data: [
|
||||
{
|
||||
title: 'online',
|
||||
icon: icon_prefix + 'qingjia1.png',
|
||||
description: '请假申请',
|
||||
useCount: 10000,
|
||||
routeIndex: 'online',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
title: '组件示例',
|
||||
icon: icon_prefix + 'chuchai.png',
|
||||
description: '出差申请',
|
||||
useCount: 10000,
|
||||
routeIndex: 'demo',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
title: '公文发文',
|
||||
icon: icon_prefix + 'gongwen.png',
|
||||
description: '公文发文',
|
||||
useCount: 10000,
|
||||
routeIndex: 'docSend',
|
||||
},
|
||||
{
|
||||
title: '通知公告',
|
||||
icon: icon_prefix + 'tongzhi.png',
|
||||
description: '查看企业对员工下发的通知公告',
|
||||
useCount: 10000,
|
||||
routeIndex: 'annotationList',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
title: '日程',
|
||||
icon: icon_prefix + 'richeng.png',
|
||||
description: '建立和查看个人工作安排',
|
||||
useCount: 10000,
|
||||
routeIndex: 'plan',
|
||||
},
|
||||
{
|
||||
title: '考勤',
|
||||
icon: icon_prefix + 'kaoqin.png',
|
||||
description: '工作考勤',
|
||||
routeIndex: 'attendance',
|
||||
useCount: 10000,
|
||||
},
|
||||
{
|
||||
title: '内部邮件',
|
||||
icon: icon_prefix + 'youjian.png',
|
||||
description: '查看内部消息',
|
||||
useCount: 10000,
|
||||
dot: false,
|
||||
routeIndex: 'mailHome',
|
||||
},
|
||||
{
|
||||
title: '通讯录',
|
||||
icon: icon_prefix + 'tongxun.png',
|
||||
description: '查看组员',
|
||||
useCount: 10000,
|
||||
//routeIndex:'addressBook',
|
||||
routeIndex: 'levelAddressBook',
|
||||
},
|
||||
{
|
||||
title: '日报',
|
||||
icon: icon_prefix + 'richang.png',
|
||||
description: '记录每天的工作经验和心得',
|
||||
useCount: 1000,
|
||||
},
|
||||
{
|
||||
title: '周报',
|
||||
icon: icon_prefix + 'zhoubao.png',
|
||||
description: '总结每周的工作情况和下周计划',
|
||||
useCount: 10000,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* other server 其他服务
|
||||
*/
|
||||
export const os = {
|
||||
data: [
|
||||
{
|
||||
title: '新闻中心',
|
||||
icon: icon_prefix + 'xinwen.png',
|
||||
description: '新闻中心',
|
||||
routeIndex: 'columnList',
|
||||
useCount: 10000,
|
||||
},
|
||||
{
|
||||
title: '文档中心',
|
||||
icon: icon_prefix + 'wendang.png',
|
||||
description: '文档中心',
|
||||
routeIndex: 'fileHome',
|
||||
useCount: 10000,
|
||||
},
|
||||
{
|
||||
title: '会议',
|
||||
icon: icon_prefix + 'huiyi.png',
|
||||
description: '会议',
|
||||
useCount: 10000,
|
||||
routeIndex: 'meeting',
|
||||
},
|
||||
{
|
||||
title: '任务中心',
|
||||
icon: icon_prefix + 'renwu.png',
|
||||
description: '任务中心',
|
||||
useCount: 10000,
|
||||
},
|
||||
{
|
||||
title: '合同',
|
||||
icon: icon_prefix + 'hetong.png',
|
||||
description: '合同',
|
||||
useCount: 10000,
|
||||
},
|
||||
// #ifndef MP-WEIXIN
|
||||
{
|
||||
title: '聊天',
|
||||
icon: icon_prefix + 'kehu.png',
|
||||
description: '聊天',
|
||||
routeIndex: 'chathome',
|
||||
},
|
||||
// #endif
|
||||
],
|
||||
}
|
||||
59
src/components/BottomOperate/BottomOperate.vue
Normal file
59
src/components/BottomOperate/BottomOperate.vue
Normal file
@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<wd-popup v-model="show" position="bottom" @close="handleClose">
|
||||
<view class="contetn">
|
||||
<wd-text v-if="title" :text="title"></wd-text>
|
||||
<wd-cell-group border>
|
||||
<wd-cell
|
||||
v-for="(item, index) in options"
|
||||
:icon="item.icon"
|
||||
:label="item.label"
|
||||
:custom-class="item.color"
|
||||
clickable
|
||||
@click="handleClick(item)"
|
||||
></wd-cell>
|
||||
</wd-cell-group>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
defineOptions({
|
||||
name: 'BottomOperate',
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const eimt = defineEmits(['change', 'close'])
|
||||
const show = ref(true)
|
||||
const props = defineProps(['title', 'data', 'options'])
|
||||
const handleClose = () => {
|
||||
show.value = false
|
||||
setTimeout(() => {
|
||||
eimt('close')
|
||||
}, 300)
|
||||
}
|
||||
const handleClick = (item) => {
|
||||
eimt('change', { option: item, data: props.data })
|
||||
handleClose()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.contetn {
|
||||
padding: 10px;
|
||||
.wd-text.is-default {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
:deep(.wd-cell) {
|
||||
padding-left: 0;
|
||||
--wot-cell-label-color: #444;
|
||||
--wot-cell-label-fs: 14px;
|
||||
&.red {
|
||||
color: red;
|
||||
--wot-cell-label-color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
258
src/components/CategorySelect/CategorySelect.vue
Normal file
258
src/components/CategorySelect/CategorySelect.vue
Normal file
@ -0,0 +1,258 @@
|
||||
<template>
|
||||
<view class="CategorySelect">
|
||||
<view @click="handleClick">
|
||||
<wd-input
|
||||
:placeholder="`请选择${$attrs.label}`"
|
||||
v-bind="$attrs"
|
||||
readonly
|
||||
v-model="showText"
|
||||
></wd-input>
|
||||
</view>
|
||||
<wd-popup position="bottom" v-model="popupShow">
|
||||
<view class="content">
|
||||
<view class="operation">
|
||||
<view class="cancel text-gray-5" @click.stop="cancel">取消</view>
|
||||
<view class="confrim" @click.stop="confirm">确定</view>
|
||||
</view>
|
||||
<scroll-view class="flex-1" scroll-y>
|
||||
<DaTree
|
||||
:data="treeData"
|
||||
labelField="title"
|
||||
valueField="key"
|
||||
loadMode
|
||||
:showCheckbox="multiple"
|
||||
:showRadioIcon="false"
|
||||
:checkStrictly="true"
|
||||
:loadApi="asyncLoadTreeData"
|
||||
@change="handleTreeChange"
|
||||
></DaTree>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, useAttrs } from 'vue'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { http } from '@/utils/http'
|
||||
import DaTree from '@/uni_modules/da-tree/index.vue'
|
||||
import { isArray } from '@/utils/is'
|
||||
defineOptions({
|
||||
name: 'CategorySelect',
|
||||
})
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [Array, String],
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择',
|
||||
required: false,
|
||||
},
|
||||
condition: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
// 是否支持多选
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
pid: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
pcode: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['change', 'update:modelValue'])
|
||||
const toast = useToast()
|
||||
const api = {
|
||||
loadDictItem: '/sys/category/loadDictItem/',
|
||||
loadTreeData: '/sys/category/loadTreeData',
|
||||
}
|
||||
const showText = ref('')
|
||||
const popupShow = ref(false)
|
||||
const treeData = ref<any[]>([])
|
||||
const treeValue = ref([])
|
||||
const handleClick = () => {
|
||||
popupShow.value = true
|
||||
}
|
||||
const cancel = () => {
|
||||
popupShow.value = false
|
||||
}
|
||||
const confirm = () => {
|
||||
const titles = treeValue.value.map((item) => item.title)
|
||||
const keys = treeValue.value.map((item) => item.key).join(',')
|
||||
showText.value = titles.join(',')
|
||||
popupShow.value = false
|
||||
emit('update:modelValue', keys)
|
||||
emit('change', keys)
|
||||
}
|
||||
const handleTreeChange = (value, record) => {
|
||||
const { originItem, checkedStatus } = record
|
||||
const { key, title } = originItem
|
||||
if (checkedStatus) {
|
||||
// 选中
|
||||
if (props.multiple) {
|
||||
treeValue.value.push({ key, title })
|
||||
} else {
|
||||
treeValue.value = [{ key, title }]
|
||||
}
|
||||
} else {
|
||||
// 取消
|
||||
if (props.multiple) {
|
||||
const findIndex = treeValue.value.findIndex((item) => item.key == key)
|
||||
if (findIndex != -1) {
|
||||
treeValue.value.splice(findIndex, 1)
|
||||
}
|
||||
} else {
|
||||
treeValue.value = []
|
||||
}
|
||||
}
|
||||
}
|
||||
const transformField = (result) => {
|
||||
for (let i of result) {
|
||||
i.value = i.key
|
||||
if (i.leaf == false) {
|
||||
i.isLeaf = false
|
||||
} else if (i.leaf == true) {
|
||||
i.isLeaf = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// 异步加载
|
||||
const asyncLoadTreeData = ({ originItem }) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
let param = {
|
||||
pid: originItem.key,
|
||||
condition: props.condition,
|
||||
}
|
||||
http
|
||||
.get(api.loadTreeData, param)
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
const { result } = res
|
||||
transformField(result)
|
||||
resolve(result)
|
||||
} else {
|
||||
resolve(null)
|
||||
}
|
||||
})
|
||||
.catch((err) => resolve(null))
|
||||
})
|
||||
}
|
||||
// 加载根节点
|
||||
function loadRoot() {
|
||||
let param = {
|
||||
pid: props.pid,
|
||||
pcode: !props.pcode ? '0' : props.pcode,
|
||||
condition: props.condition,
|
||||
}
|
||||
http
|
||||
.get(api.loadTreeData, param)
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
const { result } = res
|
||||
if (result && result.length > 0) {
|
||||
transformField(result)
|
||||
treeData.value = result
|
||||
}
|
||||
} else {
|
||||
toast.warning('分类字典书组件根节点数据加载失败~')
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.warning('分类字典书组件根节点数据加载失败~')
|
||||
})
|
||||
}
|
||||
// 翻译input内的值
|
||||
function loadItemByCode() {
|
||||
let value = props.modelValue
|
||||
if (isArray(props.modelValue)) {
|
||||
// @ts-ignore
|
||||
value = value.join()
|
||||
}
|
||||
if (value === treeData.value.map((item) => item.key).join(',')) {
|
||||
// 说明是刚选完,内部已有翻译。不需要再请求
|
||||
return
|
||||
}
|
||||
http
|
||||
.get(api.loadDictItem, { ids: value })
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
const { result = [] } = res
|
||||
showText.value = result.join(',')
|
||||
} else {
|
||||
}
|
||||
})
|
||||
.catch((err) => {})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
loadItemByCode()
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
)
|
||||
watch(
|
||||
() => props.pcode,
|
||||
() => {
|
||||
loadRoot()
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.wd-popup-wrapper) {
|
||||
.wd-popup {
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
height: 50vh;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.operation {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
line-height: 40px;
|
||||
padding: 0 5px;
|
||||
position: relative;
|
||||
&::before {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
height: 1px;
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
.cancel,
|
||||
.confrim {
|
||||
font-size: 15px;
|
||||
height: 40px;
|
||||
min-width: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
.confrim {
|
||||
color: var(--wot-color-theme);
|
||||
}
|
||||
}
|
||||
:deep(.da-tree) {
|
||||
.da-tree-item__checkbox {
|
||||
// display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
100
src/components/Grid/Grid.vue
Normal file
100
src/components/Grid/Grid.vue
Normal file
@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<view
|
||||
class="wrap"
|
||||
:style="{
|
||||
'--borderColor': borderColor,
|
||||
'--imgWidth': imgWidth,
|
||||
'--imgHeight': imgHeight,
|
||||
}"
|
||||
>
|
||||
<wd-grid :clickable="clickable" :column="column">
|
||||
<template v-for="(item, index) in modelValue" :key="item[itemKey]">
|
||||
<wd-grid-item
|
||||
:custom-class="getClass(index)"
|
||||
use-icon-slot
|
||||
:text="item.text"
|
||||
@itemclick="handleClik(item, index)"
|
||||
>
|
||||
<template #icon>
|
||||
<wd-img :width="imgWidth" :height="imgHeight" :src="item.img"></wd-img>
|
||||
</template>
|
||||
</wd-grid-item>
|
||||
</template>
|
||||
</wd-grid>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
defineOptions({
|
||||
name: 'Grid',
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
column: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
},
|
||||
itemKey: {
|
||||
type: String,
|
||||
default: 'id',
|
||||
},
|
||||
imgWidth: {
|
||||
type: String,
|
||||
default: '28px',
|
||||
},
|
||||
imgHeight: {
|
||||
type: String,
|
||||
default: '28px',
|
||||
},
|
||||
clickable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
borderColor: {
|
||||
type: String,
|
||||
default: 'rgba(165, 165, 165, 0.1)',
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['itemClik'])
|
||||
const getClass = (index) => {
|
||||
let className = ''
|
||||
if (index < props.column) {
|
||||
className = 'first-row'
|
||||
}
|
||||
if ((index + 1) % props.column == 0) {
|
||||
className += ` lastCol`
|
||||
}
|
||||
return className
|
||||
}
|
||||
const handleClik = (item, index) => {
|
||||
emit('itemClik', item, index)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.wd-grid-item) {
|
||||
box-sizing: border-box;
|
||||
border-right: 1px solid var(--borderColor, rgba(165, 165, 165, 0.1));
|
||||
border-bottom: 1px solid var(--borderColor, rgba(165, 165, 165, 0.1));
|
||||
&.first-row {
|
||||
border-top: 1px solid var(--borderColor, rgba(165, 165, 165, 0.1));
|
||||
}
|
||||
&.lastCol {
|
||||
border-right: none;
|
||||
}
|
||||
.wd-grid-item__text {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.wd-grid-item__wrapper {
|
||||
width: var(--imgWidth, 28px) !important;
|
||||
height: var(--imgHeight, 28px) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
144
src/components/ImgPreview/ImgPreview.vue
Normal file
144
src/components/ImgPreview/ImgPreview.vue
Normal file
@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<view class="previewImage" @tap="close">
|
||||
<view class="page" v-if="urls.length > 0">
|
||||
<text class="text">{{ current + 1 }} / {{ urls.length }}</text>
|
||||
</view>
|
||||
<swiper
|
||||
class="swiper"
|
||||
:current="current"
|
||||
@change="swiperChange"
|
||||
@touchstart="handleTouchStart"
|
||||
@touchend="handleTouchEnd"
|
||||
>
|
||||
<swiper-item class="swiperItem" v-for="(item, index) in urls" :key="index">
|
||||
<movable-area class="movable-area" scale-area>
|
||||
<movable-view
|
||||
class="movable-view"
|
||||
direction="all"
|
||||
:inertia="true"
|
||||
damping="100"
|
||||
scale="true"
|
||||
scale-min="1"
|
||||
scale-max="4"
|
||||
:scale-value="scale"
|
||||
>
|
||||
<scroll-view scroll-y="true" class="uni-scroll-view">
|
||||
<view class="scroll-view">
|
||||
<image
|
||||
:key="index"
|
||||
class="image"
|
||||
:src="item"
|
||||
mode="widthFix"
|
||||
@longpress="onLongpress(item)"
|
||||
/>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</movable-view>
|
||||
</movable-area>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
urls: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => {
|
||||
return []
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
current: 0, //当前页
|
||||
scale: 1,
|
||||
isZooming: false, // 是否处于缩放状态
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open(current) {
|
||||
this.current = this.urls.findIndex((item) => item === current)
|
||||
},
|
||||
//关闭
|
||||
close() {
|
||||
if (!this.isZooming) {
|
||||
this.show = false
|
||||
this.current = 0
|
||||
this.$emit('close')
|
||||
}
|
||||
},
|
||||
//图片改变
|
||||
swiperChange(e) {
|
||||
this.current = e.detail.current
|
||||
},
|
||||
//监听长按
|
||||
onLongpress(e) {
|
||||
this.$emit('onLongpress', e)
|
||||
},
|
||||
handleTouchStart() {
|
||||
this.isZooming = true
|
||||
},
|
||||
handleTouchEnd() {
|
||||
this.isZooming = false
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.previewImage {
|
||||
z-index: 9999;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000000;
|
||||
.swiper {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
.swiperItem {
|
||||
.movable-area {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.movable-view {
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
.uni-scroll-view {
|
||||
height: 100vh;
|
||||
}
|
||||
.scroll-view {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
.image {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.page {
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
width: 100%;
|
||||
top: 60rpx;
|
||||
text-align: center;
|
||||
.text {
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
padding: 3rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
193
src/components/PageLayout/PageLayout.vue
Normal file
193
src/components/PageLayout/PageLayout.vue
Normal file
@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<view class="pageLayout">
|
||||
<view
|
||||
v-if="navbarShow"
|
||||
:class="{ pageNav: true, transparent: navBgTransparent, fixed: navFixed }"
|
||||
:style="{ height: `${statusBarHeight + navHeight}px` }"
|
||||
>
|
||||
<view class="statusBar" :style="{ height: `${statusBarHeight}px` }"></view>
|
||||
<wd-navbar
|
||||
:bordered="!navBgTransparent"
|
||||
:title="navTitle"
|
||||
:leftText="navLeftText"
|
||||
:leftArrow="navLeftArrow"
|
||||
:rightText="navRightText"
|
||||
@clickLeft="handleClickLeft"
|
||||
@clickRight="handleClickRight"
|
||||
custom-class="nav"
|
||||
>
|
||||
<template v-if="$slots.navRight" #right>
|
||||
<slot name="navRight"></slot>
|
||||
</template>
|
||||
</wd-navbar>
|
||||
</view>
|
||||
<view class="pageContent">
|
||||
<slot></slot>
|
||||
</view>
|
||||
<view class="tabbar"></view>
|
||||
<wd-toast></wd-toast>
|
||||
<wd-message-box></wd-message-box>
|
||||
<wd-notify></wd-notify>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSlots } from 'vue'
|
||||
import { useRouter } from '@/plugin/uni-mini-router'
|
||||
import { useParamsStore } from '@/store/page-params'
|
||||
|
||||
defineOptions({
|
||||
name: 'pageLayout',
|
||||
options: {
|
||||
// apply-shared:当前页面样式会影响到子组件样式.(小程序)
|
||||
// shared:当前页面样式影响到子组件,子组件样式也会影响到当前页面.(小程序)
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const paramsStore = useParamsStore()
|
||||
const router = useRouter()
|
||||
const props = defineProps({
|
||||
backRouteName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
backRoutePath: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
routeParams: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
routeQuery: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
routeMethod: {
|
||||
type: String,
|
||||
default: 'replace',
|
||||
},
|
||||
navbarShow: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
navBgTransparent: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
navFixed: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'page', //'page','popup'
|
||||
},
|
||||
navTitle: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
navLeftText: {
|
||||
type: String,
|
||||
default: '返回',
|
||||
},
|
||||
navLeftArrow: {
|
||||
typeof: Boolean,
|
||||
default: true,
|
||||
},
|
||||
navRightText: {
|
||||
typeof: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
const slot = useSlots()
|
||||
const globalData = getApp().globalData
|
||||
const { systemInfo, navHeight } = globalData
|
||||
const { statusBarHeight } = systemInfo
|
||||
const emit = defineEmits(['navBack', 'navRight'])
|
||||
const handleClickLeft = () => {
|
||||
emit('navBack')
|
||||
// 只有在页面中才默认返回,弹层中不返回
|
||||
if (props.type === 'page') {
|
||||
const pages = getCurrentPages()
|
||||
if (props.backRouteName || props.backRoutePath) {
|
||||
const prevPage = pages[pages.length - 2]
|
||||
if (prevPage) {
|
||||
const route = prevPage.route
|
||||
const name = route.split('/').pop()
|
||||
if (route === props.backRoutePath || props.backRouteName === name) {
|
||||
router.back()
|
||||
clearPageParamsCache()
|
||||
return
|
||||
}
|
||||
}
|
||||
if (props.backRouteName) {
|
||||
router[props.routeMethod]({ name: props.backRouteName, params: props.routeParams })
|
||||
clearPageParamsCache()
|
||||
} else {
|
||||
router[props.routeMethod]({ name: props.backRoutePath, query: props.routeQuery })
|
||||
clearPageParamsCache()
|
||||
}
|
||||
} else {
|
||||
router.back()
|
||||
clearPageParamsCache()
|
||||
}
|
||||
}
|
||||
}
|
||||
const clearPageParamsCache = () => {
|
||||
// 清除页面传参缓存
|
||||
const pages = getCurrentPages()
|
||||
const curPage = pages[pages.length - 1]
|
||||
const curRoute = curPage.route
|
||||
const name = curRoute.split('/').pop()
|
||||
paramsStore.clearPageParams(name)
|
||||
}
|
||||
const handleClickRight = () => {
|
||||
emit('navRight')
|
||||
}
|
||||
console.log('props:', props)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pageLayout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
.pageNav {
|
||||
background-image: linear-gradient(45deg, #0081ff, #1cbbb4);
|
||||
&.transparent {
|
||||
background-image: none;
|
||||
}
|
||||
&.fixed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.statusBar {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
}
|
||||
:deep(.wd-navbar) {
|
||||
background-color: transparent;
|
||||
--wot-navbar-title-font-weight: 400;
|
||||
--wot-navbar-arrow-size: 18px;
|
||||
--wot-navbar-desc-font-size: 14px;
|
||||
--wot-navbar-title-font-size: 16px;
|
||||
}
|
||||
}
|
||||
.pageContent {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
.tabbar {
|
||||
/* #ifdef H5 */
|
||||
height: var(--window-bottom);
|
||||
/* #endif */
|
||||
}
|
||||
}
|
||||
</style>
|
||||
118
src/components/Popup/Popup.vue
Normal file
118
src/components/Popup/Popup.vue
Normal file
@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<view class="Popup">
|
||||
<view @click="handleClick">
|
||||
<wd-input
|
||||
:placeholder="`请选择${$attrs.label}`"
|
||||
type="text"
|
||||
readonly
|
||||
v-model="showText"
|
||||
clearable
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</view>
|
||||
<popupReportModal
|
||||
v-if="reportModal.show"
|
||||
:code="code"
|
||||
:showFiled="reportModal.showFiled"
|
||||
:multi="multi"
|
||||
@close="handleClose"
|
||||
@change="handleChange"
|
||||
></popupReportModal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, useAttrs } from 'vue'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { http } from '@/utils/http'
|
||||
import popupReportModal from './components/popupReportModal.vue'
|
||||
defineOptions({
|
||||
name: 'Popup',
|
||||
options: {
|
||||
styleIsolation: 'shared'
|
||||
}
|
||||
})
|
||||
const props = defineProps({
|
||||
code: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '',
|
||||
},
|
||||
fieldConfig: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
},
|
||||
setFieldsValue: {
|
||||
type: Function,
|
||||
required: true,
|
||||
default: () => {},
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
multi: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
spliter: {
|
||||
type: String,
|
||||
default: ',',
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['change', 'update:modelValue'])
|
||||
|
||||
const toast = useToast()
|
||||
const showText = ref('')
|
||||
const attrs: any = useAttrs()
|
||||
const reportModal = reactive({
|
||||
show: false,
|
||||
showFiled: props.fieldConfig.map((item) => item['target']).join(','),
|
||||
})
|
||||
if (!props.code || props.fieldConfig.length == 0) {
|
||||
toast.error('popup参数未正确配置!')
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听value数值
|
||||
*/
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
showText.value = val && val.length > 0 ? val.split(props.spliter).join(',') : ''
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
function callBack(rows) {
|
||||
let fieldConfig: any = props.fieldConfig
|
||||
//匹配popup设置的回调值
|
||||
let values = {}
|
||||
let labels = []
|
||||
for (let item of fieldConfig) {
|
||||
let val = rows.map((row) => row[item.source])
|
||||
val = val.length == 1 ? val[0] : val.join(',')
|
||||
item.target.split(',').forEach((target) => {
|
||||
values[target] = val
|
||||
})
|
||||
}
|
||||
showText.value = labels.join(',')
|
||||
props.setFieldsValue(values)
|
||||
emit('change', values)
|
||||
// emit('update:modelValue', values)
|
||||
}
|
||||
const handleClick = () => {
|
||||
if (!attrs.disabled) {
|
||||
reportModal.show = true
|
||||
}
|
||||
}
|
||||
const handleClose = () => {
|
||||
reportModal.show = false
|
||||
}
|
||||
const handleChange = (data) => {
|
||||
console.log('选中的值:', data)
|
||||
callBack(data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
290
src/components/Popup/components/popupReportModal.vue
Normal file
290
src/components/Popup/components/popupReportModal.vue
Normal file
@ -0,0 +1,290 @@
|
||||
<template>
|
||||
<wd-popup position="bottom" v-model="show">
|
||||
<PageLayout
|
||||
:navTitle="navTitle"
|
||||
type="popup"
|
||||
navRightText="确定"
|
||||
@navRight="handleConfirm"
|
||||
@navBack="handleCancel"
|
||||
>
|
||||
<view class="wrap">
|
||||
<z-paging
|
||||
ref="paging"
|
||||
:fixed="false"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
:default-page-size="15"
|
||||
>
|
||||
<template #top>
|
||||
<wd-search
|
||||
hide-cancel
|
||||
:placeholder="search.placeholder"
|
||||
v-model="search.keyword"
|
||||
@search="handleSearch"
|
||||
@clear="handleClear"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="multi">
|
||||
<wd-checkbox-group shape="square" v-model="checkedValue">
|
||||
<template v-for="(item, index) in dataList" :key="index">
|
||||
<view class="list" @click="hanldeCheck(index)">
|
||||
<view class="left text-gray-5">
|
||||
<template v-for="(cItem, cIndex) in columns" :key="cIndex">
|
||||
<view class="row">
|
||||
<text class="label">{{ cItem.title }}:</text>
|
||||
<text class="value">{{ item[cItem.dataIndex] }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
<view class="right" @click.stop>
|
||||
<wd-checkbox ref="checkboxRef" :modelValue="index"></wd-checkbox>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</wd-checkbox-group>
|
||||
</template>
|
||||
<template v-else>
|
||||
<wd-radio-group shape="dot" v-model="checkedValue">
|
||||
<template v-for="(item, index) in dataList" :key="index">
|
||||
<wd-cell>
|
||||
<view class="list" @click="hanldeCheck(index)">
|
||||
<view class="left text-gray-5">
|
||||
<template v-for="(cItem, cIndex) in columns" :key="cIndex">
|
||||
<view class="row">
|
||||
<text class="label">{{ cItem.title }}:</text>
|
||||
<text class="value">{{ item[cItem.dataIndex] }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
<view class="right" @click.stop>
|
||||
<wd-radio :value="index"></wd-radio>
|
||||
</view>
|
||||
</view>
|
||||
</wd-cell>
|
||||
</template>
|
||||
</wd-radio-group>
|
||||
</template>
|
||||
</z-paging>
|
||||
</view>
|
||||
</PageLayout>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { http } from '@/utils/http'
|
||||
import { isArray } from '@/utils/is'
|
||||
defineOptions({
|
||||
name: 'popupReportModal',
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const props = defineProps({
|
||||
code: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true,
|
||||
},
|
||||
showFiled: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true,
|
||||
},
|
||||
multi: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['change', 'close'])
|
||||
const toast = useToast()
|
||||
const show = ref(true)
|
||||
const api = {
|
||||
getColumns: '/online/cgreport/api/getRpColumns',
|
||||
getData: '/online/cgreport/api/getData',
|
||||
getQueryInfo: '/online/cgreport/api/getQueryInfo',
|
||||
}
|
||||
console.log('props:::', props)
|
||||
const navTitle = ref('')
|
||||
const paging = ref(null)
|
||||
const dataList = ref([])
|
||||
// 报表id
|
||||
let rpConfigId = null
|
||||
let loadedColumns = false
|
||||
const dictOptions = ref([])
|
||||
const columns = ref([])
|
||||
const checkedValue: any = ref(props.multi ? [] : '')
|
||||
const checkboxRef = ref(null)
|
||||
const search = reactive({
|
||||
keyword: '',
|
||||
placeholder: '',
|
||||
field: '',
|
||||
})
|
||||
|
||||
const handleClose = () => {
|
||||
setTimeout(() => {
|
||||
emit('close')
|
||||
}, 400)
|
||||
}
|
||||
const handleConfirm = () => {
|
||||
if (checkedValue.value.length == 0) {
|
||||
toast.warning('还没选择~')
|
||||
return
|
||||
}
|
||||
const result = []
|
||||
let value = checkedValue.value
|
||||
if (!Array.isArray(checkedValue.value)) {
|
||||
value = [checkedValue.value]
|
||||
}
|
||||
value.forEach((index) => {
|
||||
result.push(dataList.value[index])
|
||||
})
|
||||
show.value = false
|
||||
emit('change', result)
|
||||
handleClose()
|
||||
}
|
||||
const handleCancel = () => {
|
||||
show.value = false
|
||||
handleClose()
|
||||
console.log('取消了~')
|
||||
}
|
||||
// 搜索
|
||||
function handleSearch() {
|
||||
paging.value.reload()
|
||||
}
|
||||
// 清除搜索条件
|
||||
function handleClear() {
|
||||
search.keyword = ''
|
||||
handleSearch()
|
||||
}
|
||||
const hanldeCheck = (index) => {
|
||||
if (props.multi) {
|
||||
if (Array.isArray(checkboxRef.value)) {
|
||||
checkboxRef.value[index].toggle()
|
||||
}
|
||||
} else {
|
||||
checkedValue.value = index
|
||||
}
|
||||
}
|
||||
const getQueryInfo = () => {
|
||||
const analysis = (data = []) => {
|
||||
if (data.length) {
|
||||
search.placeholder = `请输入${data[0].label}`
|
||||
search.field = data[0].field
|
||||
} else {
|
||||
const item = columns[0] ?? {}
|
||||
search.placeholder = `请输入${item.title}`
|
||||
search.field = item.dataIndex
|
||||
}
|
||||
}
|
||||
http
|
||||
.get(`${api.getQueryInfo}/${rpConfigId}`)
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
analysis(res.result)
|
||||
} else {
|
||||
analysis()
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
analysis()
|
||||
})
|
||||
}
|
||||
const getRpColumns = () => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (loadedColumns) {
|
||||
resolve()
|
||||
} else {
|
||||
http
|
||||
.get(`${api.getColumns}/${props.code}`)
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
loadedColumns = true
|
||||
const { result } = res
|
||||
navTitle.value = result.cgRpConfigName
|
||||
dictOptions.value = result.dictOptions
|
||||
rpConfigId = result.cgRpConfigId
|
||||
const fileds = props.showFiled.split(',')
|
||||
result.columns?.forEach((item) => {
|
||||
if (fileds.includes(item.dataIndex)) {
|
||||
columns.value.push(item)
|
||||
}
|
||||
})
|
||||
getQueryInfo()
|
||||
resolve()
|
||||
} else {
|
||||
reject()
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
reject()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const queryList = (pageNo, pageSize) => {
|
||||
const pararms = { pageNo, pageSize }
|
||||
if (search.keyword) {
|
||||
pararms[search.field] = `*${search.keyword}*`
|
||||
}
|
||||
getRpColumns()
|
||||
.then(() => {
|
||||
http
|
||||
.get(`${api.getData}/${rpConfigId}`, pararms)
|
||||
.then((res: any) => {
|
||||
if (res.success && res.result.records) {
|
||||
paging.value.complete(res.result.records ?? [])
|
||||
} else {
|
||||
paging.value.complete(false)
|
||||
}
|
||||
})
|
||||
.catch((err) => {})
|
||||
})
|
||||
.catch((err) => {})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.wd-cell) {
|
||||
--wot-color-white: tranparent;
|
||||
--wot-cell-padding: 0;
|
||||
.wd-cell__wrapper {
|
||||
--wot-cell-wrapper-padding: 0;
|
||||
}
|
||||
.wd-cell__left {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
:deep(.wd-checkbox-group) {
|
||||
--wot-checkbox-bg: tranparent;
|
||||
}
|
||||
:deep(.wd-radio-group) {
|
||||
--wot-radio-bg: tranparent;
|
||||
}
|
||||
.list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #fff;
|
||||
padding: 16px;
|
||||
margin-top: 16px;
|
||||
.left {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
.row {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
:deep(.wd-checkbox) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.wrap {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
186
src/components/PopupDict/PopupDict.vue
Normal file
186
src/components/PopupDict/PopupDict.vue
Normal file
@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<view class="PopupDict">
|
||||
<view @click.stop="handleClick">
|
||||
<wd-select-picker
|
||||
v-model="showText"
|
||||
:columns="options"
|
||||
readonly
|
||||
:type="multi ? 'checkbox' : 'radio'"
|
||||
@click="() => (reportModal.show = true)"
|
||||
v-bind="$attrs"
|
||||
></wd-select-picker>
|
||||
</view>
|
||||
<popupReportModal
|
||||
v-if="reportModal.show"
|
||||
:code="code"
|
||||
:showFiled="labelFiled"
|
||||
:multi="multi"
|
||||
@close="handleClose"
|
||||
@change="handleChange"
|
||||
></popupReportModal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { http } from '@/utils/http'
|
||||
import popupReportModal from '@/components/Popup/components/popupReportModal.vue'
|
||||
defineOptions({
|
||||
name: 'PopupDict',
|
||||
options: {
|
||||
styleIsolation: 'shared'
|
||||
}
|
||||
})
|
||||
const props = defineProps({
|
||||
dictCode: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '',
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
multi: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
spliter: {
|
||||
type: String,
|
||||
default: ',',
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['change', 'update:modelValue'])
|
||||
|
||||
const toast = useToast()
|
||||
const showText = ref<any>(props.multi ? [] : '')
|
||||
const options = ref<any>([])
|
||||
const cgRpConfigId = ref('')
|
||||
const code = ref(props.dictCode.split(',')[0])
|
||||
const labelFiled = ref(props.dictCode.split(',')[1])
|
||||
const valueFiled = ref(props.dictCode.split(',')[2])
|
||||
const reportModal = reactive({
|
||||
show: false,
|
||||
})
|
||||
//定义请求url信息
|
||||
const configUrl = reactive({
|
||||
getColumns: '/online/cgreport/api/getRpColumns/',
|
||||
getData: '/online/cgreport/api/getData/',
|
||||
})
|
||||
|
||||
if (!code.value || !valueFiled.value || !labelFiled.value) {
|
||||
toast.error('popupDict参数未正确配置!')
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听value数值
|
||||
*/
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
const callBack = () => {
|
||||
if (props.multi) {
|
||||
showText.value = val && val.length > 0 ? val.split(props.spliter) : []
|
||||
} else {
|
||||
showText.value = val ?? ''
|
||||
}
|
||||
}
|
||||
if (props.modelValue) {
|
||||
if (cgRpConfigId.value) {
|
||||
loadData({ callBack })
|
||||
} else {
|
||||
loadColumnsInfo({ callBack })
|
||||
}
|
||||
} else {
|
||||
callBack()
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
watch(
|
||||
() => showText.value,
|
||||
(val) => {
|
||||
let result
|
||||
if (props.multi) {
|
||||
result = val.join(',')
|
||||
} else {
|
||||
result = val
|
||||
}
|
||||
nextTick(() => {
|
||||
emit('change', result)
|
||||
emit('update:modelValue', result)
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
/**
|
||||
* 加载列信息
|
||||
*/
|
||||
function loadColumnsInfo({ callBack }) {
|
||||
let url = `${configUrl.getColumns}${code.value}`
|
||||
http
|
||||
.get(url)
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
cgRpConfigId.value = res.result.cgRpConfigId
|
||||
loadData({ callBack })
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
callBack?.()
|
||||
})
|
||||
}
|
||||
function loadData({ callBack }) {
|
||||
let url = `${configUrl.getData}${unref(cgRpConfigId)}`
|
||||
http
|
||||
.get(url, { ['force_' + valueFiled.value]: props.modelValue })
|
||||
.then((res: any) => {
|
||||
let data = res.result
|
||||
if (data.records?.length) {
|
||||
options.value = data.records.map((item) => {
|
||||
return { value: item[valueFiled.value], label: item[labelFiled.value] }
|
||||
})
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
callBack?.()
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 传值回调
|
||||
*/
|
||||
function callBack(rows) {
|
||||
const dataOptions: any = []
|
||||
const dataValue: any = []
|
||||
let result
|
||||
rows.forEach((item) => {
|
||||
dataOptions.push({ value: item[valueFiled.value], label: item[labelFiled.value] })
|
||||
dataValue.push(item[valueFiled.value])
|
||||
})
|
||||
options.value = dataOptions
|
||||
if (props.multi) {
|
||||
showText.value = dataValue
|
||||
result = dataValue.join(props.spliter)
|
||||
} else {
|
||||
showText.value = dataValue[0]
|
||||
result = dataValue[0]
|
||||
}
|
||||
nextTick(() => {
|
||||
emit('change', result)
|
||||
emit('update:modelValue', result)
|
||||
})
|
||||
}
|
||||
const handleClick = () => {
|
||||
reportModal.show = true
|
||||
}
|
||||
const handleClose = () => {
|
||||
reportModal.show = false
|
||||
}
|
||||
const handleChange = (data) => {
|
||||
console.log('选中的值:', data)
|
||||
callBack(data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
84
src/components/ProgressMap/ProgressMap.vue
Normal file
84
src/components/ProgressMap/ProgressMap.vue
Normal file
@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<view class="ProgressMap">
|
||||
<view class="title">{{ title }}</view>
|
||||
<view
|
||||
:class="{
|
||||
stepBox: true,
|
||||
active: item.activeStep,
|
||||
'u-iconfont': true,
|
||||
'u-icon-clock': !item.activeStep,
|
||||
'u-icon-success': item.activeStep,
|
||||
}"
|
||||
v-for="(item, index) in dataSource"
|
||||
:key="index"
|
||||
>
|
||||
<view :class="{ stepContent: true, active: item.activeStep }">
|
||||
<view class="item" v-for="(inItem, inIndex) in item.data" :key="inIndex">
|
||||
{{ inItem.label }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
padding: 20upx;
|
||||
font-weight: bold;
|
||||
}
|
||||
.stepBox {
|
||||
position: relative;
|
||||
padding: 32upx 32upx 32upx 120upx;
|
||||
color: #aaaaaa;
|
||||
&.active {
|
||||
color: #39b54a;
|
||||
}
|
||||
&::before {
|
||||
position: absolute;
|
||||
left: 40upx;
|
||||
background-color: #fff;
|
||||
z-index: 9;
|
||||
top: 40upx;
|
||||
border-radius: 50%;
|
||||
margin: 8upx;
|
||||
}
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 0.5px;
|
||||
background-color: #ddd;
|
||||
left: 60upx;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
z-index: 8;
|
||||
}
|
||||
.stepContent {
|
||||
padding: 10upx 50upx 10upx 30upx;
|
||||
border-radius: 8upx;
|
||||
background-color: #f0f0f0;
|
||||
color: #333333;
|
||||
font-size: 24upx;
|
||||
&.active {
|
||||
background-color: #39b54a;
|
||||
color: #fff;
|
||||
box-shadow: 0 0 5px #39b54a;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
68
src/components/RightConditionFilter/RightConditionFilter.vue
Normal file
68
src/components/RightConditionFilter/RightConditionFilter.vue
Normal file
@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<wd-popup v-model="show" position="right" @close="handleClose">
|
||||
<view class="content">
|
||||
<wd-text v-if="title" :text="title"></wd-text>
|
||||
<wd-cell-group border>
|
||||
<wd-radio-group v-model="checked">
|
||||
<template v-for="(item, index) in options">
|
||||
<wd-cell :title="item.title" clickable @click="handleSelected(item)">
|
||||
<wd-radio :value="item.key"></wd-radio>
|
||||
</wd-cell>
|
||||
</template>
|
||||
</wd-radio-group>
|
||||
</wd-cell-group>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { hasRoute, cache } from '@/common/uitls'
|
||||
import { ref } from 'vue'
|
||||
const eimt = defineEmits(['change', 'close'])
|
||||
const show = ref(true)
|
||||
const props = defineProps(['title', 'data', 'options', 'checked'])
|
||||
const checked = ref(props.checked)
|
||||
const handleClose = () => {
|
||||
show.value = false
|
||||
setTimeout(() => {
|
||||
eimt('close')
|
||||
}, 300)
|
||||
}
|
||||
const handleSelected = (item) => {
|
||||
checked.value = item.key
|
||||
eimt('change', { option: item, data: props.data })
|
||||
handleClose()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
max-width: 200px;
|
||||
padding: 10px;
|
||||
.wd-text.is-default {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
.wd-cell {
|
||||
padding-left: 0;
|
||||
--wot-cell-label-color: #444;
|
||||
--wot-cell-label-fs: 14px;
|
||||
&.red {
|
||||
color: red;
|
||||
--wot-cell-label-color: red;
|
||||
}
|
||||
}
|
||||
.wd-cell-group {
|
||||
:deep(.wd-cell__wrapper) {
|
||||
align-items: center;
|
||||
.wd-cell__right {
|
||||
flex: none;
|
||||
width: 24px;
|
||||
}
|
||||
.wd-radio {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
251
src/components/SelectDept/SelectDept.vue
Normal file
251
src/components/SelectDept/SelectDept.vue
Normal file
@ -0,0 +1,251 @@
|
||||
<template>
|
||||
<view class="CategorySelect">
|
||||
<view @click="handleClick">
|
||||
<wd-input
|
||||
:placeholder="getPlaceholder($attrs)"
|
||||
v-bind="$attrs"
|
||||
v-model="showText"
|
||||
clearable
|
||||
readonly
|
||||
></wd-input>
|
||||
</view>
|
||||
<wd-popup position="bottom" v-model="popupShow">
|
||||
<view class="content">
|
||||
<view class="operation">
|
||||
<view class="cancel text-gray-5" @click.stop="cancel">取消</view>
|
||||
<view class="confrim" @click.stop="confirm">确定</view>
|
||||
</view>
|
||||
<scroll-view class="flex-1" scroll-y>
|
||||
<DaTree
|
||||
:data="treeData"
|
||||
:labelField="labelKey"
|
||||
:valueField="rowKey"
|
||||
loadMode
|
||||
:showCheckbox="multiple"
|
||||
:showRadioIcon="false"
|
||||
:checkStrictly="true"
|
||||
:loadApi="asyncLoadTreeData"
|
||||
@change="handleTreeChange"
|
||||
></DaTree>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, useAttrs } from 'vue'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { http } from '@/utils/http'
|
||||
import DaTree from '@/uni_modules/da-tree/index.vue'
|
||||
import { isArray } from '@/utils/is'
|
||||
import { getPlaceholder } from '@/common/uitls'
|
||||
defineOptions({
|
||||
name: 'SelectDept',
|
||||
})
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [Array, String],
|
||||
},
|
||||
// 是否支持多选
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
rowKey: {
|
||||
type: String,
|
||||
default: 'key',
|
||||
},
|
||||
labelKey: {
|
||||
type: String,
|
||||
default: 'title',
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['change', 'update:modelValue'])
|
||||
const toast = useToast()
|
||||
const api = {
|
||||
loadDictItem: '/sys/category/loadDictItem/',
|
||||
queryDepartTreeSync: '/sys/sysDepart/queryDepartTreeSync',
|
||||
}
|
||||
const showText = ref('')
|
||||
const popupShow = ref(false)
|
||||
const treeData = ref<any[]>([])
|
||||
const treeValue = ref([])
|
||||
const handleClick = () => {
|
||||
popupShow.value = true
|
||||
}
|
||||
const cancel = () => {
|
||||
popupShow.value = false
|
||||
}
|
||||
const confirm = () => {
|
||||
const titles = treeValue.value.map((item) => item.title)
|
||||
const keys = treeValue.value.map((item) => item.key).join(',')
|
||||
showText.value = titles.join(',')
|
||||
popupShow.value = false
|
||||
emit('update:modelValue', keys)
|
||||
emit('change', keys)
|
||||
}
|
||||
const handleClear = (params) => {
|
||||
console.log('params:::', params)
|
||||
}
|
||||
const handleTreeChange = (value, record) => {
|
||||
const { originItem, checkedStatus } = record
|
||||
if (checkedStatus) {
|
||||
// 选中
|
||||
if (props.multiple) {
|
||||
treeValue.value.push({ key: originItem[props.rowKey], title: originItem[props.labelKey] })
|
||||
} else {
|
||||
treeValue.value = [{ key: originItem[props.rowKey], title: originItem[props.labelKey] }]
|
||||
}
|
||||
} else {
|
||||
// 取消
|
||||
if (props.multiple) {
|
||||
const findIndex = treeValue.value.findIndex(
|
||||
(item) => item[props.rowKey] == originItem[props.rowKey],
|
||||
)
|
||||
if (findIndex != -1) {
|
||||
treeValue.value.splice(findIndex, 1)
|
||||
}
|
||||
} else {
|
||||
treeValue.value = []
|
||||
}
|
||||
}
|
||||
}
|
||||
const transformField = (result) => {
|
||||
for (let i of result) {
|
||||
i.value = i.key
|
||||
if (i.isLeaf == false) {
|
||||
i.leaf = false
|
||||
} else if (i.isLeaf == true) {
|
||||
i.leaf = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// 异步加载
|
||||
const asyncLoadTreeData = ({ originItem }) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
let param = {
|
||||
pid: originItem.key,
|
||||
primaryKey: props.rowKey,
|
||||
}
|
||||
http
|
||||
.get(api.queryDepartTreeSync, param)
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
const { result } = res
|
||||
transformField(result)
|
||||
resolve(result)
|
||||
} else {
|
||||
resolve(null)
|
||||
}
|
||||
})
|
||||
.catch((err) => resolve(null))
|
||||
})
|
||||
}
|
||||
// 加载根节点
|
||||
function loadRoot() {
|
||||
let param = {
|
||||
primaryKey: props.rowKey,
|
||||
}
|
||||
http
|
||||
.get(api.queryDepartTreeSync, param)
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
const { result } = res
|
||||
if (result && result.length > 0) {
|
||||
transformField(result)
|
||||
treeData.value = result
|
||||
}
|
||||
} else {
|
||||
console.error('部门组件加载根节点数据失败~')
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('部门组件加载根节点数据失败~')
|
||||
})
|
||||
}
|
||||
// 翻译input内的值
|
||||
function loadItemByCode() {
|
||||
let value = props.modelValue
|
||||
console.log('部门组件翻译props.modelValue', props.modelValue)
|
||||
if (isArray(props.modelValue)) {
|
||||
// @ts-ignore
|
||||
value = value.join(',')
|
||||
}
|
||||
if (value === treeData.value.map((item) => item.key).join(',')) {
|
||||
// 说明是刚选完,内部已有翻译。不需要再请求
|
||||
return
|
||||
}
|
||||
http
|
||||
.get(api.queryDepartTreeSync, { ids: value })
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
const { result = [] } = res
|
||||
showText.value = result.map((item) => item[props.labelKey]).join(',')
|
||||
} else {
|
||||
}
|
||||
})
|
||||
.catch((err) => {})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
loadItemByCode()
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
)
|
||||
watch(
|
||||
() => props.pcode,
|
||||
() => {
|
||||
loadRoot()
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.wd-popup-wrapper) {
|
||||
.wd-popup {
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
height: 50vh;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.operation {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
line-height: 40px;
|
||||
padding: 0 5px;
|
||||
position: relative;
|
||||
&::before {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
height: 1px;
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
.cancel,
|
||||
.confrim {
|
||||
font-size: 15px;
|
||||
height: 40px;
|
||||
min-width: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
.confrim {
|
||||
color: var(--wot-color-theme);
|
||||
}
|
||||
}
|
||||
:deep(.da-tree) {
|
||||
.da-tree-item__checkbox {
|
||||
// display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
117
src/components/SelectUser/SelectUser.vue
Normal file
117
src/components/SelectUser/SelectUser.vue
Normal file
@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<view class="SelectUser">
|
||||
<view @click.stop="handleClick">
|
||||
<wd-input
|
||||
:placeholder="getPlaceholder($attrs)"
|
||||
v-bind="$attrs"
|
||||
readonly
|
||||
v-model="showText"
|
||||
></wd-input>
|
||||
</view>
|
||||
<SelectUserModal
|
||||
v-if="modalShow"
|
||||
:selected="modelValue"
|
||||
:modalTitle="modalTitle"
|
||||
:maxSelectCount="maxSelectCount"
|
||||
:multi="!isRadioSelection"
|
||||
@change="handleChange"
|
||||
@close="() => (modalShow = false)"
|
||||
></SelectUserModal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, useAttrs } from 'vue'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { http } from '@/utils/http'
|
||||
import DaTree from '@/uni_modules/da-tree/index.vue'
|
||||
import { isArray, isString } from '@/utils/is'
|
||||
import SelectUserModal from './components/SelectUserModal.vue'
|
||||
import { getPlaceholder } from '@/common/uitls'
|
||||
defineOptions({
|
||||
name: 'SelectUser',
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [Array, String],
|
||||
default: '',
|
||||
},
|
||||
labelKey: {
|
||||
type: String,
|
||||
default: 'realname',
|
||||
},
|
||||
rowKey: {
|
||||
type: String,
|
||||
default: 'username',
|
||||
},
|
||||
isRadioSelection: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
modalTitle: {
|
||||
type: String,
|
||||
default: '选择用户',
|
||||
},
|
||||
maxSelectCount: {
|
||||
type: Number,
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
const api = {
|
||||
list: '/sys/user/list',
|
||||
}
|
||||
const showText = ref('')
|
||||
const modalShow = ref(false)
|
||||
|
||||
// 翻译
|
||||
const transform = () => {
|
||||
let value = props.modelValue
|
||||
let len
|
||||
if (isArray(value) || isString(value)) {
|
||||
if (isArray(value)) {
|
||||
len = value.length
|
||||
value = value.join(',')
|
||||
} else {
|
||||
len = value.split(',').length
|
||||
}
|
||||
value = value.trim()
|
||||
if (value) {
|
||||
const params = { isMultiTranslate: true, pageSize: len, [props.rowKey]: value }
|
||||
http.get(api.list, params).then((res: any) => {
|
||||
if (res.success) {
|
||||
const records = res.result?.records ?? []
|
||||
showText.value = records.map((item) => item[props.labelKey]).join(',')
|
||||
} else {
|
||||
console.log('翻译失败~')
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
showText.value = ''
|
||||
}
|
||||
}
|
||||
// 打开popup
|
||||
const handleClick = () => {
|
||||
modalShow.value = true
|
||||
}
|
||||
const handleChange = (data) => {
|
||||
const rowkey = data.map((item) => item[props.rowKey]).join(',')
|
||||
const labelKey = data.map((item) => item[props.labelKey]).join(',')
|
||||
showText.value = labelKey
|
||||
emit('update:modelValue', rowkey)
|
||||
emit('change', rowkey)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
transform()
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
282
src/components/SelectUser/components/SelectUserModal.vue
Normal file
282
src/components/SelectUser/components/SelectUserModal.vue
Normal file
@ -0,0 +1,282 @@
|
||||
<template>
|
||||
<wd-popup position="bottom" v-model="show">
|
||||
<PageLayout
|
||||
:navTitle="modalTitle"
|
||||
type="popup"
|
||||
navRightText="确定"
|
||||
@navRight="handleConfirm"
|
||||
@navBack="handleCancel"
|
||||
>
|
||||
<view class="wrap">
|
||||
<z-paging
|
||||
ref="paging"
|
||||
:fixed="false"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
:default-page-size="15"
|
||||
>
|
||||
<template #top>
|
||||
<wd-search
|
||||
hide-cancel
|
||||
:placeholder="search.placeholder"
|
||||
v-model="search.keyword"
|
||||
@search="handleSearch"
|
||||
@clear="handleClear"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="multi">
|
||||
<wd-checkbox-group shape="square" v-model="checkedValue">
|
||||
<template v-for="(item, index) in dataList" :key="index">
|
||||
<view class="list" @click="hanldeCheck(index, item.username)">
|
||||
<view class="left text-gray-5">
|
||||
<wd-img
|
||||
custom-class="avatar"
|
||||
radius="50%"
|
||||
height="40"
|
||||
width="40"
|
||||
:src="getAvatar(item.avatar)"
|
||||
></wd-img>
|
||||
<view class="subContent">
|
||||
<text>账号:{{ item.username }}</text>
|
||||
<text>姓名:{{ item.realname }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="right" @click.stop>
|
||||
<wd-checkbox ref="checkboxRef" :modelValue="item.username"></wd-checkbox>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</wd-checkbox-group>
|
||||
</template>
|
||||
<template v-else>
|
||||
<wd-radio-group shape="dot" v-model="checkedValue">
|
||||
<template v-for="(item, index) in dataList" :key="index">
|
||||
<wd-cell>
|
||||
<view class="list" @click="hanldeCheck(index, item.username)">
|
||||
<view class="left text-gray-5">
|
||||
<wd-img
|
||||
custom-class="avatar"
|
||||
radius="50%"
|
||||
height="40"
|
||||
width="40"
|
||||
:src="getAvatar(item.avatar)"
|
||||
></wd-img>
|
||||
<view class="subContent">
|
||||
<text>账号:{{ item.username }}</text>
|
||||
<text>姓名:{{ item.realname }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="right" @click.stop>
|
||||
<wd-radio :value="item.username"></wd-radio>
|
||||
</view>
|
||||
</view>
|
||||
</wd-cell>
|
||||
</template>
|
||||
</wd-radio-group>
|
||||
</template>
|
||||
</z-paging>
|
||||
</view>
|
||||
</PageLayout>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, nextTick } from 'vue'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { http } from '@/utils/http'
|
||||
import { isArray, isString } from '@/utils/is'
|
||||
import { cache, getFileAccessHttpUrl } from '@/common/uitls'
|
||||
import defaultAvatar from '@/static/default-avatar.png'
|
||||
defineOptions({
|
||||
name: 'SelectUserModal',
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const props = defineProps({
|
||||
multi: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
modalTitle: {
|
||||
type: String,
|
||||
default: '选择用户',
|
||||
},
|
||||
maxSelectCount: {
|
||||
type: Number,
|
||||
},
|
||||
selected: {
|
||||
type: [Array, String],
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['change', 'close'])
|
||||
const toast = useToast()
|
||||
const show = ref(true)
|
||||
const api = {
|
||||
selectUserList: '/sys/user/selectUserList',
|
||||
userlist: '/sys/user/list',
|
||||
}
|
||||
const paging = ref(null)
|
||||
const dataList = ref([])
|
||||
const checkedValue: any = ref(props.multi ? [] : '')
|
||||
const checkboxRef = ref(null)
|
||||
const search = reactive({
|
||||
keyword: '',
|
||||
placeholder: '输入姓名可搜索',
|
||||
field: 'realname',
|
||||
})
|
||||
|
||||
const handleClose = () => {
|
||||
setTimeout(() => {
|
||||
emit('close')
|
||||
}, 400)
|
||||
}
|
||||
const handleConfirm = () => {
|
||||
if (checkedValue.value.length == 0) {
|
||||
toast.warning('还没选择~')
|
||||
return
|
||||
}
|
||||
const result = []
|
||||
let value = checkedValue.value
|
||||
if (!Array.isArray(checkedValue.value)) {
|
||||
value = [checkedValue.value]
|
||||
}
|
||||
value.forEach((username, index) => {
|
||||
const findIndex = dataList.value.findIndex((item) => item['username'] === username)
|
||||
result.push(dataList.value[findIndex])
|
||||
})
|
||||
show.value = false
|
||||
emit('change', result)
|
||||
handleClose()
|
||||
}
|
||||
const handleCancel = () => {
|
||||
show.value = false
|
||||
handleClose()
|
||||
console.log('取消了~')
|
||||
}
|
||||
// 搜索
|
||||
function handleSearch() {
|
||||
paging.value.reload()
|
||||
}
|
||||
// 清除搜索条件
|
||||
function handleClear() {
|
||||
search.keyword = ''
|
||||
handleSearch()
|
||||
}
|
||||
const hanldeCheck = (index, username) => {
|
||||
if (props.multi) {
|
||||
if (Array.isArray(checkboxRef.value)) {
|
||||
checkboxRef.value[index].toggle()
|
||||
nextTick(() => {
|
||||
if (props.maxSelectCount) {
|
||||
if (checkedValue.value.length > props.maxSelectCount) {
|
||||
toast.warning(`最多可选择${props.maxSelectCount}个用户`)
|
||||
// 超过个数取消
|
||||
checkboxRef.value[index].toggle()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
checkedValue.value = username
|
||||
}
|
||||
}
|
||||
|
||||
const getAvatar = (url) => {
|
||||
let result = getFileAccessHttpUrl(url)
|
||||
if (result.length) {
|
||||
return result
|
||||
} else {
|
||||
return defaultAvatar
|
||||
}
|
||||
}
|
||||
|
||||
const queryList = (pageNo, pageSize) => {
|
||||
const pararms = { pageNo, pageSize, column: 'createTime', order: 'desc' }
|
||||
if (search.keyword) {
|
||||
pararms[search.field] = `*${search.keyword}*`
|
||||
}
|
||||
http
|
||||
.get(`${api.userlist}`, pararms)
|
||||
.then((res: any) => {
|
||||
if (res.success && res.result.records) {
|
||||
paging.value.complete(res.result.records ?? [])
|
||||
} else {
|
||||
paging.value.complete(false)
|
||||
}
|
||||
})
|
||||
.catch((err) => {})
|
||||
}
|
||||
const init = () => {
|
||||
if (props.selected.length) {
|
||||
if (props.multi) {
|
||||
if (isArray(props.selected)) {
|
||||
checkedValue.value = props.selected
|
||||
} else if (isString(props.selected)) {
|
||||
checkedValue.value = props.selected.split(',')
|
||||
}
|
||||
} else {
|
||||
if (isString(props.selected)) {
|
||||
checkedValue.value = props.selected
|
||||
} else if (isArray(props.selected)) {
|
||||
checkedValue.value = props.selected.join(',')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
init()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.wd-cell) {
|
||||
--wot-color-white: tranparent;
|
||||
--wot-cell-padding: 0;
|
||||
.wd-cell__wrapper {
|
||||
--wot-cell-wrapper-padding: 0;
|
||||
}
|
||||
.wd-cell__left {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
:deep(.wd-checkbox-group) {
|
||||
--wot-checkbox-bg: tranparent;
|
||||
}
|
||||
:deep(.wd-radio-group) {
|
||||
--wot-radio-bg: tranparent;
|
||||
}
|
||||
.list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #fff;
|
||||
padding: 16px;
|
||||
margin-top: 16px;
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
.avatar {
|
||||
margin-right: 8px;
|
||||
background-color: #e9e9e9;
|
||||
}
|
||||
.subContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
:deep(.wd-checkbox) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.wrap {
|
||||
height: 100%;
|
||||
}
|
||||
:deep(.wd-popup-wrapper) {
|
||||
.wd-popup {
|
||||
top: 100px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
346
src/components/TreeSelect/TreeSelect.vue
Normal file
346
src/components/TreeSelect/TreeSelect.vue
Normal file
@ -0,0 +1,346 @@
|
||||
<template>
|
||||
<view class="TreeSelect">
|
||||
<view @click="handleClick">
|
||||
<wd-input
|
||||
:placeholder="`请选择${$attrs.label}`"
|
||||
v-bind="$attrs"
|
||||
readonly
|
||||
v-model="showText"
|
||||
></wd-input>
|
||||
</view>
|
||||
<wd-popup position="bottom" v-model="popupShow">
|
||||
<view class="content">
|
||||
<view class="operation">
|
||||
<view class="cancel text-gray-5" @click.stop="cancel">取消</view>
|
||||
<view class="confrim" @click.stop="confirm">确定</view>
|
||||
</view>
|
||||
<scroll-view class="flex-1" scroll-y>
|
||||
<DaTree
|
||||
:data="treeData"
|
||||
labelField="title"
|
||||
valueField="key"
|
||||
loadMode
|
||||
:showCheckbox="multiple"
|
||||
:showRadioIcon="false"
|
||||
:checkStrictly="true"
|
||||
:loadApi="asyncLoadTreeData"
|
||||
@change="handleTreeChange"
|
||||
></DaTree>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, useAttrs } from 'vue'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { http } from '@/utils/http'
|
||||
import DaTree from '@/uni_modules/da-tree/index.vue'
|
||||
import { isArray } from '@/utils/is'
|
||||
defineOptions({
|
||||
name: 'TreeSelect',
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [Array, String],
|
||||
},
|
||||
dict: {
|
||||
type: String,
|
||||
default: 'id',
|
||||
},
|
||||
pidValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
pidField: {
|
||||
type: String,
|
||||
default: 'pid',
|
||||
},
|
||||
hasChildField: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
condition: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
converIsLeafVal: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
// 是否支持多选
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
hiddenNodeKey: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// url: {
|
||||
// type: String,
|
||||
// default: '',
|
||||
// },
|
||||
// params: {
|
||||
// type: Object,
|
||||
// default: () => ({}),
|
||||
// },
|
||||
})
|
||||
const emit = defineEmits(['change', 'update:modelValue'])
|
||||
const toast = useToast()
|
||||
const api = {
|
||||
loadTreeData: '/sys/dict/loadTreeData',
|
||||
view: '/sys/dict/loadDictItem/',
|
||||
}
|
||||
const showText = ref('')
|
||||
const popupShow = ref(false)
|
||||
const treeData = ref<any[]>([])
|
||||
const treeValue = ref([])
|
||||
const tableName = ref<any>('')
|
||||
const text = ref<any>('')
|
||||
const code = ref<any>('')
|
||||
|
||||
const handleClick = () => {
|
||||
popupShow.value = true
|
||||
}
|
||||
const cancel = () => {
|
||||
popupShow.value = false
|
||||
}
|
||||
const confirm = () => {
|
||||
const titles = treeValue.value.map((item) => item.title)
|
||||
const keys = treeValue.value.map((item) => item.key).join(',')
|
||||
showText.value = titles.join(',')
|
||||
popupShow.value = false
|
||||
emit('update:modelValue', keys)
|
||||
emit('change', keys)
|
||||
}
|
||||
const handleTreeChange = (value, record) => {
|
||||
const { originItem, checkedStatus } = record
|
||||
const { key, title } = originItem
|
||||
if (checkedStatus) {
|
||||
// 选中
|
||||
if (props.multiple) {
|
||||
treeValue.value.push({ key, title })
|
||||
} else {
|
||||
treeValue.value = [{ key, title }]
|
||||
}
|
||||
} else {
|
||||
// 取消
|
||||
if (props.multiple) {
|
||||
const findIndex = treeValue.value.findIndex((item) => item.key == key)
|
||||
if (findIndex != -1) {
|
||||
treeValue.value.splice(findIndex, 1)
|
||||
}
|
||||
} else {
|
||||
treeValue.value = []
|
||||
}
|
||||
}
|
||||
}
|
||||
const transformField = (result) => {
|
||||
for (let i of result) {
|
||||
i.value = i.key
|
||||
if (i.leaf == false) {
|
||||
i.isLeaf = false
|
||||
} else if (i.leaf == true) {
|
||||
i.isLeaf = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// 异步加载
|
||||
const asyncLoadTreeData = ({ originItem }) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
let param = {
|
||||
pid: originItem.key,
|
||||
pidField: props.pidField,
|
||||
hasChildField: props.hasChildField,
|
||||
converIsLeafVal: props.converIsLeafVal,
|
||||
condition: props.condition,
|
||||
tableName: unref(tableName),
|
||||
text: unref(text),
|
||||
code: unref(code),
|
||||
}
|
||||
http
|
||||
.get(api.loadTreeData, param)
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
const { result } = res
|
||||
transformField(result)
|
||||
resolve(result)
|
||||
} else {
|
||||
resolve(null)
|
||||
}
|
||||
})
|
||||
.catch((err) => resolve(null))
|
||||
})
|
||||
}
|
||||
// 加载根节点
|
||||
function loadRoot() {
|
||||
let param = {
|
||||
pid: props.pidValue,
|
||||
pidField: props.pidField,
|
||||
hasChildField: props.hasChildField,
|
||||
condition: props.condition,
|
||||
converIsLeafVal: props.converIsLeafVal,
|
||||
tableName: unref(tableName),
|
||||
text: unref(text),
|
||||
code: unref(code),
|
||||
}
|
||||
http
|
||||
.get(api.loadTreeData, param)
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
const { result } = res
|
||||
if (result && result.length > 0) {
|
||||
transformField(result)
|
||||
handleHiddenNode(result)
|
||||
treeData.value = result
|
||||
}
|
||||
} else {
|
||||
toast.warning('自定义树组件根节点数据加载失败~')
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.warning('自定义树组件根节点数据加载失败~')
|
||||
})
|
||||
}
|
||||
// 翻译input内的值
|
||||
function loadItemByCode() {
|
||||
let value = props.modelValue
|
||||
if (isArray(props.modelValue)) {
|
||||
// @ts-ignore
|
||||
value = value.join()
|
||||
}
|
||||
if (value === treeData.value.map((item) => item.key).join(',')) {
|
||||
// 说明是刚选完,内部已有翻译。不需要再请求
|
||||
return
|
||||
}
|
||||
http
|
||||
.get(`${api.view}${props.dict}`, { key: value })
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
const { result = [] } = res
|
||||
showText.value = result.join(',')
|
||||
} else {
|
||||
}
|
||||
})
|
||||
.catch((err) => {})
|
||||
}
|
||||
const initDictInfo = () => {
|
||||
let arr = props.dict?.split(',')
|
||||
tableName.value = arr[0]
|
||||
text.value = arr[1]
|
||||
code.value = arr[2]
|
||||
}
|
||||
const handleHiddenNode = (data) => {
|
||||
if (props.hiddenNodeKey && data?.length) {
|
||||
for (let i = 0, len = data.length; i < len; i++) {
|
||||
const item = data[i]
|
||||
if (item.key == props.hiddenNodeKey) {
|
||||
data.splice(i, 1)
|
||||
i--
|
||||
len--
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const validateProp = () => {
|
||||
let mycondition = props.condition
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (!mycondition) {
|
||||
resolve()
|
||||
} else {
|
||||
try {
|
||||
let test = JSON.parse(mycondition)
|
||||
if (typeof test == 'object' && test) {
|
||||
resolve()
|
||||
} else {
|
||||
toast.error('组件TreeSelect-condition传值有误,需要一个json字符串!')
|
||||
reject()
|
||||
}
|
||||
} catch (e) {
|
||||
toast.error('组件TreeSelect-condition传值有误,需要一个json字符串!')
|
||||
reject()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => loadItemByCode(),
|
||||
{ deep: true, immediate: true },
|
||||
)
|
||||
watch(
|
||||
() => props.dict,
|
||||
() => {
|
||||
initDictInfo()
|
||||
loadRoot()
|
||||
},
|
||||
)
|
||||
watch(
|
||||
() => props.hiddenNodeKey,
|
||||
() => {
|
||||
if (treeData.value?.length && props.hiddenNodeKey) {
|
||||
handleHiddenNode(treeData.value)
|
||||
treeData.value = [...treeData.value]
|
||||
}
|
||||
},
|
||||
)
|
||||
// 初始化
|
||||
validateProp().then(() => {
|
||||
initDictInfo()
|
||||
loadRoot()
|
||||
loadItemByCode()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.wd-popup-wrapper) {
|
||||
.wd-popup {
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
height: 50vh;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.operation {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
line-height: 40px;
|
||||
padding: 0 5px;
|
||||
position: relative;
|
||||
&::before {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
height: 1px;
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
.cancel,
|
||||
.confrim {
|
||||
font-size: 15px;
|
||||
height: 40px;
|
||||
min-width: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
.confrim {
|
||||
color: var(--wot-color-theme);
|
||||
}
|
||||
}
|
||||
:deep(.da-tree) {
|
||||
.da-tree-item__checkbox {
|
||||
// display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
169
src/components/online/FormProperty.ts
Normal file
169
src/components/online/FormProperty.ts
Normal file
@ -0,0 +1,169 @@
|
||||
// 定义 FormProperty 函数
|
||||
const FormProperty = (propertyId, formSchema, required = []) => {
|
||||
// 初始化私有属性
|
||||
const _propertyId = propertyId;
|
||||
const _formSchem = formSchema;
|
||||
const _required = required;
|
||||
|
||||
// 定义 formSchema 的 getter 方法
|
||||
const getFormSchema = () => {
|
||||
return _formSchem || {};
|
||||
};
|
||||
|
||||
// 定义 key 的 getter 方法
|
||||
const getKey = () => {
|
||||
return _propertyId;
|
||||
};
|
||||
|
||||
// 定义 type 的 getter 方法
|
||||
const getType = () => {
|
||||
return getFormSchema().view;
|
||||
};
|
||||
|
||||
// 定义 disabled 的 getter 方法
|
||||
const getDisabled = () => {
|
||||
if (_formSchem && _formSchem.ui && _formSchem.ui.widgetattrs && _formSchem.ui.widgetattrs.disabled === true) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// 定义 label 的 getter 方法
|
||||
const getLabel = () => {
|
||||
const schema = getFormSchema();
|
||||
return schema.title || getKey();
|
||||
};
|
||||
|
||||
// 定义 placeholder 的 getter 方法
|
||||
const getPlaceholder = () => {
|
||||
const viewType = getType();
|
||||
const label = getLabel();
|
||||
if (viewType.indexOf('date') >= 0 || viewType.indexOf('select') >= 0 || viewType.indexOf('list') >= 0) {
|
||||
return "请选择" + label;
|
||||
} else if (viewType.indexOf('upload') >= 0 || viewType.indexOf('file') >= 0 || viewType.indexOf('image') >= 0) {
|
||||
return "请上传" + label;
|
||||
} else {
|
||||
return "请输入" + label;
|
||||
}
|
||||
};
|
||||
|
||||
// 定义 dictStr 的 getter 方法
|
||||
const getDictStr = () => {
|
||||
const viewType = getType();
|
||||
if (viewType === 'sel_search') {
|
||||
const schema = getFormSchema();
|
||||
return schema.dictTable + ',' + schema.dictText + ',' + schema.dictCode;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
// 定义 listSource 的 getter 方法
|
||||
const getListSource = () => {
|
||||
const schema = getFormSchema();
|
||||
if (!schema.enum) {
|
||||
return [];
|
||||
}
|
||||
const arr = [...schema.enum];
|
||||
for (let a = 0; a < arr.length; a++) {
|
||||
if (!arr[a].label) {
|
||||
arr[a].label = arr[a].text;
|
||||
}
|
||||
if (schema.type === 'number') {
|
||||
arr[a].value = parseInt(arr[a].value);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
// 定义 popupCode 的 getter 方法
|
||||
const getPopupCode = () => {
|
||||
return getFormSchema().code;
|
||||
};
|
||||
|
||||
// 定义 dest 的 getter 方法
|
||||
const getDest = () => {
|
||||
return getFormSchema().destFields;
|
||||
};
|
||||
|
||||
// 定义 ogn 的 getter 方法
|
||||
const getOgn = () => {
|
||||
return getFormSchema().orgFields;
|
||||
};
|
||||
|
||||
// 定义 rules 的 getter 方法
|
||||
const getRules = () => {
|
||||
const rules = [];
|
||||
const isRequired = _required?.includes(getKey()) ?? false;
|
||||
if (isRequired) {
|
||||
let msg = getLabel() + '为必填项';
|
||||
rules.push({ required: true, message: msg });
|
||||
}
|
||||
let viewType = getType();
|
||||
if ('list' === viewType || 'markdown' === viewType || 'pca' === viewType) {
|
||||
return rules;
|
||||
}
|
||||
if (viewType.indexOf('upload') >= 0 || viewType.indexOf('file') >= 0 || viewType.indexOf('image') >= 0) {
|
||||
return rules;
|
||||
}
|
||||
|
||||
const schema = getFormSchema();
|
||||
if (schema.pattern) {
|
||||
if (schema.pattern === 'only') {
|
||||
// 这里 checkOnlyMethod 未定义,需要根据实际情况补充
|
||||
rules.push({ validator: () => {} });
|
||||
} else if (schema.pattern === 'z') {
|
||||
if (schema.type === 'number' || schema.type === 'integer') {
|
||||
// 这里 onlyInteger 未定义,需要根据实际情况处理
|
||||
} else {
|
||||
rules.push({ pattern: '^-?[1-9]\\d*$', message: '请输入整数' });
|
||||
}
|
||||
} else {
|
||||
let msg = getLabel() + '校验未通过';
|
||||
rules.push({ pattern: schema.pattern, message: msg });
|
||||
}
|
||||
}
|
||||
return rules;
|
||||
};
|
||||
|
||||
// 返回包含所有 getter 方法的对象
|
||||
return {
|
||||
get formSchema() {
|
||||
return getFormSchema();
|
||||
},
|
||||
get key() {
|
||||
return getKey();
|
||||
},
|
||||
get type() {
|
||||
return getType();
|
||||
},
|
||||
get disabled() {
|
||||
return getDisabled();
|
||||
},
|
||||
get label() {
|
||||
return getLabel();
|
||||
},
|
||||
get placeholder() {
|
||||
return getPlaceholder();
|
||||
},
|
||||
get dictStr() {
|
||||
return getDictStr();
|
||||
},
|
||||
get listSource() {
|
||||
return getListSource();
|
||||
},
|
||||
get popupCode() {
|
||||
return getPopupCode();
|
||||
},
|
||||
get dest() {
|
||||
return getDest();
|
||||
},
|
||||
get ogn() {
|
||||
return getOgn();
|
||||
},
|
||||
get rules() {
|
||||
return getRules();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default FormProperty;
|
||||
14978
src/components/online/area-picker-data.ts
Normal file
14978
src/components/online/area-picker-data.ts
Normal file
File diff suppressed because it is too large
Load Diff
297
src/components/online/defaultVal.ts
Normal file
297
src/components/online/defaultVal.ts
Normal file
@ -0,0 +1,297 @@
|
||||
|
||||
// 移动端不支持自定义表达式设置的默认值
|
||||
import {formatDate} from "@/utils";
|
||||
import {http} from "@/utils/http";
|
||||
import {useUserStore} from "@/store";
|
||||
|
||||
const CustomExpression = {}
|
||||
// 获取所有用户自定义表达式的Key
|
||||
const ceKeys = Object.keys(CustomExpression)
|
||||
// 将key用逗号拼接,可以拼接成方法参数,例:a,b,c --> function(a,b,c){}
|
||||
const ceJoin = ceKeys.join(',')
|
||||
// 将用户自定义的表达式按key的顺序放到数组中,可以使用 apply 传递给方法直接调用
|
||||
const $CE$ = ceKeys.map(key => CustomExpression[key])
|
||||
|
||||
/** 普通规则表达式 #{...} */
|
||||
const normalRegExp = /#{([^}]+)?}/g
|
||||
/** 用户自定义规则表达式 {{...}} */
|
||||
const customRegExp = /{{([^}]+)?}}/g
|
||||
/** 填值规则表达式 ${...} */
|
||||
const fillRuleRegExp = /\${([^}]+)?}/g
|
||||
|
||||
/** action 类型 */
|
||||
export const ACTION_TYPES = { ADD: 'add', EDIT: 'edit', DETAIL: 'detail', RELOAD: 'reload' }
|
||||
|
||||
/**
|
||||
* 获取单个字段的默认值-通过回调函数返回值
|
||||
* @param {Object} defVal
|
||||
* @param {Object} type
|
||||
* @param {Object} callback
|
||||
*/
|
||||
export async function loadOneFieldDefVal(defVal, type, callback) {
|
||||
if(hasEffectiveValue(defVal)){
|
||||
let value = await handleDefaultValue(defVal, ACTION_TYPES.ADD, {});
|
||||
if ('number' === type && value) {
|
||||
value = Number.parseFloat(value)
|
||||
}
|
||||
callback(value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断给定的值是不是有效的
|
||||
*/
|
||||
function hasEffectiveValue(val) {
|
||||
if(val || val === 0){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认值
|
||||
* @param {Object} defVal
|
||||
* @param {Object} action
|
||||
* @param {Object} getFormData
|
||||
*/
|
||||
async function handleDefaultValue(defVal, action, getFormData) {
|
||||
if (defVal != null) {
|
||||
// 检查类型,如果类型错误则不继续运行
|
||||
if (checkExpressionType(defVal)) {
|
||||
let value = await getDefaultValue(defVal, action, getFormData)
|
||||
if (value != null) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
return defVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载form组件默认值
|
||||
* @param form Form对象
|
||||
* @param properties 字段配置
|
||||
* @param action 操作类型(ACTION_TYPES),除填值规则外,其他表达式只在add下才执行
|
||||
* @param getFormData 获取数据的方法,用于填值规则向后台传值
|
||||
*/
|
||||
export function loadFieldDefVal({ form, properties, action, getFormData }) {
|
||||
if (Array.isArray(properties) && properties.length > 0) {
|
||||
properties.forEach(async prop => {
|
||||
let { defVal, type } = prop._formSchem
|
||||
// key取值错误导致 树形表 表单默认值未生效 【online】树列表不支持控件默认值表达式配置 (博威)
|
||||
let key = prop.key
|
||||
// 2021年5月21日 Tree类型表单,系统编码不生效。【issues/I3NR39】
|
||||
if (!key) {
|
||||
key = prop._propertyId
|
||||
}
|
||||
eachHandler(key, defVal, action, (value) => {
|
||||
// 处理数字类型,如果type=number并且value有值
|
||||
if ('number' === type && value) {
|
||||
// parseFloat() 可以直接处理字符串、整数、小数、null和undefined,
|
||||
// 非数字类型直接返回NaN,不必担心报错
|
||||
value = Number.parseFloat(value)
|
||||
}
|
||||
form.setFieldsValue({ [key]: value })
|
||||
}, getFormData)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** 加载JEditableTable组件默认值 */
|
||||
export function loadFieldDefValForSubTable({ subForms, subTable, row, action, getFormData }) {
|
||||
if (subTable && Array.isArray(subTable.columns) && subTable.columns.length > 0) {
|
||||
subTable.columns.forEach(async column => {
|
||||
let { key, fieldDefaultValue: defVal } = column
|
||||
eachHandler(key, defVal, action, (value) => {
|
||||
if (subForms.form) {
|
||||
subForms.form.setFieldsValue({ [key]: value })
|
||||
} else {
|
||||
// update-begin---author:sunjianlei Date:20200725 for:online功能测试,行操作切换成新的行编辑-----------
|
||||
let v = [{rowKey: row.id, values: {[key]: value}}];
|
||||
(subForms.jvt || subForms.jet).setValues(v)
|
||||
// update-end---author:sunjianlei Date:20200725 for:online功能测试,行操作切换成新的行编辑------------
|
||||
}
|
||||
}, getFormData)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function eachHandler(key, defVal, action, callback, getFormData) {
|
||||
if (defVal != null) {
|
||||
// 检查类型,如果类型错误则不继续运行
|
||||
if (checkExpressionType(defVal)) {
|
||||
let value = await getDefaultValue(defVal, action, getFormData)
|
||||
if (value != null) {
|
||||
callback(value)
|
||||
}
|
||||
} else {
|
||||
// 不合法的表达式直接返回不解析
|
||||
callback(defVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查表达式类型是否合法,规则:
|
||||
* 1、填值规则表达式不能和其他表达式混用
|
||||
* 2、每次只能填写一个填值规则表达式
|
||||
* 3、普通表达式和用户自定义表达式可以混用
|
||||
*/
|
||||
export function checkExpressionType(defVal) {
|
||||
// 获取各个表达式的数量
|
||||
let normalCount = 0, customCount = 0, fillRuleCount = 0
|
||||
defVal.replace(fillRuleRegExp, () => fillRuleCount++)
|
||||
if (fillRuleCount > 1) {
|
||||
logWarn(`表达式[${defVal}]不合法:只能同时填写一个填值规则表达式!`)
|
||||
return false
|
||||
}
|
||||
defVal.replace(normalRegExp, () => normalCount++)
|
||||
defVal.replace(customRegExp, () => customCount++)
|
||||
// 除填值规则外其他规则的数量
|
||||
let fillRuleOtherCount = normalCount + customCount
|
||||
if (fillRuleCount > 0 && fillRuleOtherCount > 0) {
|
||||
logWarn(`表达式[${defVal}]不合法:填值规则表达式不能和其他表达式混用!`)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/** 获取所有匹配的表达式 */
|
||||
function getRegExpMap(text, exp) {
|
||||
let map = new Map()
|
||||
if(text && text.length>0){
|
||||
text.replace(exp, function (match, param, offset, string) {
|
||||
map.set(match, param.trim())
|
||||
return match
|
||||
})
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
/** 获取默认值,可以执行表达式,可以执行用户自定义方法,可以异步获取用户信息等 */
|
||||
async function getDefaultValue(defVal, action, getFormData) {
|
||||
// 只有在 add 和 reload 模式下才执行填值规则
|
||||
if (action === ACTION_TYPES.ADD || action === ACTION_TYPES.RELOAD) {
|
||||
// 判断是否是填值规则表达式,如果是就执行填值规则
|
||||
if (fillRuleRegExp.test(defVal)) {
|
||||
return await executeRegExp(defVal, fillRuleRegExp, executeFillRuleExpression, [getFormData])
|
||||
}
|
||||
}
|
||||
// 只有在 add 模式下才执行其他表达式
|
||||
if (action === ACTION_TYPES.ADD) {
|
||||
// 获取并替换所有常规表达式
|
||||
defVal = await executeRegExp(defVal, normalRegExp, executeNormalExpression)
|
||||
// 获取并替换所有用户自定义表达式
|
||||
defVal = await executeRegExp(defVal, customRegExp, executeCustomExpression)
|
||||
return defVal
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
async function executeRegExp(defVal, regExp, execFun, otherParams = []) {
|
||||
let map = getRegExpMap(defVal, regExp)
|
||||
// @ts-ignore
|
||||
for (let origin of map.keys()) {
|
||||
let exp = map.get(origin)
|
||||
let result = await execFun.apply(null, [exp, origin, ...otherParams])
|
||||
// 如果只有一个表达式,那么就不替换(因为一旦替换,类型就会被转成String),直接返回执行结果,保证返回的类型不变
|
||||
if (origin === defVal) {
|
||||
return result
|
||||
}
|
||||
defVal = replaceAll(defVal, origin, result)
|
||||
}
|
||||
return defVal
|
||||
}
|
||||
|
||||
/** 执行【普通表达式】#{xxx} */
|
||||
async function executeNormalExpression(expression, origin) {
|
||||
let temp = new Date();
|
||||
switch (expression) {
|
||||
case 'date':
|
||||
return formatDate(temp, 'yyyy-MM-dd');
|
||||
case 'time':
|
||||
return formatDate(temp, 'HH:mm:ss');
|
||||
case 'datetime':
|
||||
return formatDate(temp, 'yyyy-MM-dd HH:mm:ss');
|
||||
default:
|
||||
// 获取当前登录用户的信息
|
||||
let result = getUserInfoByExpression(expression)
|
||||
if (result != null) {
|
||||
return result
|
||||
}
|
||||
// 没有符合条件的表达式,返回原始值
|
||||
return origin
|
||||
}
|
||||
}
|
||||
|
||||
/** 根据表达式获取相应的用户信息 */
|
||||
function getUserInfoByExpression(expression) {
|
||||
let userInfo:any = useUserStore().userInfo;
|
||||
if (userInfo) {
|
||||
switch (expression) {
|
||||
case 'sysUserId':
|
||||
return userInfo.id
|
||||
// 当前登录用户登录账号
|
||||
case 'sysUserCode':
|
||||
return userInfo.username
|
||||
// 当前登录用户真实名称
|
||||
case 'sysUserName':
|
||||
return userInfo.realname
|
||||
// 当前登录用户部门编号
|
||||
case 'sysOrgCode':
|
||||
return userInfo.orgCode
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/** 执行【用户自定义表达式】 {{xxx}} 移动端不支持 */
|
||||
async function executeCustomExpression(expression, origin) {
|
||||
return expression;
|
||||
// 利用 eval 生成一个方法,这个方法的参数就是用户自定义的所有的表达式
|
||||
/* let fn = eval(`(function (${ceJoin}){ return ${expression} })`)
|
||||
try {
|
||||
// 然后调用这个方法,并把表达式传递进去,从而完成表达式的执行
|
||||
return fn.apply(null, $CE$)
|
||||
} catch (e) {
|
||||
// 执行失败,输出错误并返回原始值
|
||||
logError(e)
|
||||
return origin
|
||||
} */
|
||||
}
|
||||
|
||||
/** 执行【填值规则表达式】 ${xxx} */
|
||||
async function executeFillRuleExpression(expression, origin, getFormData) {
|
||||
let url = `/sys/fillRule/executeRuleByCode/${expression}`
|
||||
let formData = {}
|
||||
if (typeof getFormData === 'function') {
|
||||
formData = getFormData()
|
||||
}
|
||||
let res:any = await http.put(url, formData)
|
||||
let { success, message, result } = res;
|
||||
console.log(success, message, result)
|
||||
if (success) {
|
||||
return result
|
||||
} else {
|
||||
logError(`填值规则(${expression})执行失败:${message}`)
|
||||
return origin
|
||||
}
|
||||
}
|
||||
|
||||
function logWarn(message) {
|
||||
console.warn('[loadFieldDefVal]:', message)
|
||||
}
|
||||
|
||||
function logError(message) {
|
||||
console.error('[loadFieldDefVal]:', message)
|
||||
}
|
||||
|
||||
function replaceAll(text, checker, replacer) {
|
||||
let lastText = text
|
||||
text = text.replace(checker, replacer)
|
||||
if (lastText !== text) {
|
||||
return replaceAll(text, checker, replacer)
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
914
src/components/online/online-loader.vue
Normal file
914
src/components/online/online-loader.vue
Normal file
@ -0,0 +1,914 @@
|
||||
<template>
|
||||
<view class="onlineLoader-container">
|
||||
<view class="form-container">
|
||||
<wd-form ref="form" :model="formData">
|
||||
<wd-cell-group border>
|
||||
<view
|
||||
class="onlineLoader-form"
|
||||
v-for="(item, index) in rootProperties"
|
||||
:key="index"
|
||||
:class="{ 'mt-14px': index % 2 == 0 }"
|
||||
>
|
||||
<!-- 图片 -->
|
||||
<wd-cell
|
||||
v-if="item.type == 'image'"
|
||||
:name="item.key"
|
||||
:title="get4Label(item.label)"
|
||||
:title-width="labelWidth"
|
||||
:required="fieldRequired(item)"
|
||||
>
|
||||
<online-image
|
||||
v-model:value="formData[item.key]"
|
||||
:name="item.key"
|
||||
:disabled="componentDisabled(item)"
|
||||
:key="index"
|
||||
></online-image>
|
||||
</wd-cell>
|
||||
|
||||
<!-- 文件 -->
|
||||
<wd-cell
|
||||
v-else-if="item.type == 'file'"
|
||||
:name="item.key"
|
||||
:title="get4Label(item.label)"
|
||||
:title-width="labelWidth"
|
||||
:required="fieldRequired(item)"
|
||||
>
|
||||
<online-image
|
||||
v-model:value="formData[item.key]"
|
||||
uploadFileType="all"
|
||||
:name="item.key"
|
||||
:disabled="componentDisabled(item)"
|
||||
:key="index"
|
||||
></online-image>
|
||||
</wd-cell>
|
||||
|
||||
<!-- 日期时间 -->
|
||||
<online-datetime
|
||||
v-else-if="item.type === 'datetime'"
|
||||
:label="get4Label(item.label)"
|
||||
:labelWidth="labelWidth"
|
||||
:name="item.key"
|
||||
:disabled="componentDisabled(item)"
|
||||
v-model:value="formData[item.key]"
|
||||
:required="fieldRequired(item)"
|
||||
></online-datetime>
|
||||
|
||||
<!-- 日期 -->
|
||||
<online-date
|
||||
v-else-if="item.type === 'date'"
|
||||
:label="get4Label(item.label)"
|
||||
:labelWidth="labelWidth"
|
||||
:name="item.key"
|
||||
:type="getDateExtendType(item.formSchema)"
|
||||
:disabled="componentDisabled(item)"
|
||||
v-model:value="formData[item.key]"
|
||||
:required="fieldRequired(item)"
|
||||
></online-date>
|
||||
|
||||
<!-- 时间 -->
|
||||
<online-time
|
||||
v-else-if="item.type === 'time'"
|
||||
:label="get4Label(item.label)"
|
||||
:labelWidth="labelWidth"
|
||||
:name="item.key"
|
||||
:disabled="componentDisabled(item)"
|
||||
v-model:value="formData[item.key]"
|
||||
:required="fieldRequired(item)"
|
||||
></online-time>
|
||||
|
||||
<!-- 下拉选择 -->
|
||||
<online-select
|
||||
v-else-if="item.type === 'list' || item.type === 'sel_search'"
|
||||
:label="get4Label(item.label)"
|
||||
:labelWidth="labelWidth"
|
||||
:name="item.key"
|
||||
:type="item.type"
|
||||
:dict="item.listSource"
|
||||
:dictStr="item.dictStr"
|
||||
:disabled="componentDisabled(item)"
|
||||
v-model="formData[item.key]"
|
||||
:required="fieldRequired(item)"
|
||||
></online-select>
|
||||
|
||||
<!-- checkbox -->
|
||||
<online-checkbox
|
||||
v-else-if="item.type === 'checkbox'"
|
||||
:name="item.key"
|
||||
:type="item.type"
|
||||
:label="get4Label(item.label)"
|
||||
:labelWidth="labelWidth"
|
||||
:dict="item.listSource"
|
||||
:disabled="componentDisabled(item)"
|
||||
:required="fieldRequired(item)"
|
||||
v-model="formData[item.key]"
|
||||
></online-checkbox>
|
||||
|
||||
<!-- radio -->
|
||||
<online-radio
|
||||
v-else-if="item.type === 'radio'"
|
||||
:name="item.key"
|
||||
:label="get4Label(item.label)"
|
||||
:labelWidth="labelWidth"
|
||||
:type="item.type"
|
||||
:dict="item.listSource"
|
||||
:disabled="componentDisabled(item)"
|
||||
:required="fieldRequired(item)"
|
||||
v-model="formData[item.key]"
|
||||
></online-radio>
|
||||
|
||||
<!-- 下拉多选 -->
|
||||
<online-multi
|
||||
v-else-if="item.type === 'list_multi'"
|
||||
:label="get4Label(item.label)"
|
||||
:labelWidth="labelWidth"
|
||||
:name="item.key"
|
||||
:dict="item.listSource"
|
||||
:disabled="componentDisabled(item)"
|
||||
:required="fieldRequired(item)"
|
||||
v-model="formData[item.key]"
|
||||
></online-multi>
|
||||
|
||||
<!-- 省市区 -->
|
||||
<online-pca
|
||||
v-else-if="item.type === 'pca'"
|
||||
:name="item.key"
|
||||
:label="get4Label(item.label)"
|
||||
:labelWidth="labelWidth"
|
||||
:disabled="componentDisabled(item)"
|
||||
:required="fieldRequired(item)"
|
||||
v-model:value="formData[item.key]"
|
||||
></online-pca>
|
||||
|
||||
<!-- 数字框 小数 -->
|
||||
<wd-input
|
||||
v-else-if="item.type === 'number' && (!item.onlyInteger || item.onlyInteger == false)"
|
||||
:label-width="labelWidth"
|
||||
v-model="formData[item.key]"
|
||||
:label="get4Label(item.label)"
|
||||
:name="item.key"
|
||||
inputMode="decimal"
|
||||
:disabled="componentDisabled(item)"
|
||||
:placeholder="item.placeholder"
|
||||
:rules="item.rules"
|
||||
/>
|
||||
|
||||
<!-- 数字框 整数 -->
|
||||
<wd-input
|
||||
v-else-if="item.type === 'number' && item.onlyInteger === true"
|
||||
:label-width="labelWidth"
|
||||
:label="get4Label(item.label)"
|
||||
:name="item.key"
|
||||
v-model="formData[item.key]"
|
||||
inputMode="numeric"
|
||||
:disabled="componentDisabled(item)"
|
||||
:placeholder="item.placeholder"
|
||||
:rules="item.rules"
|
||||
/>
|
||||
|
||||
<!-- 开关 -->
|
||||
<wd-cell
|
||||
v-else-if="item.type == 'switch'"
|
||||
:name="item.key"
|
||||
:title="get4Label(item.label)"
|
||||
:title-width="labelWidth"
|
||||
center
|
||||
:required="fieldRequired(item)"
|
||||
>
|
||||
<view style="text-align: left">
|
||||
<wd-switch
|
||||
:label="get4Label(item.label)"
|
||||
:name="item.key"
|
||||
size="18px"
|
||||
:disabled="componentDisabled(item)"
|
||||
v-model="formData[item.key]"
|
||||
:active-value="switchOpt(item.formSchema?.extendOption, 0)"
|
||||
:inactive-value="switchOpt(item.formSchema?.extendOption, 1)"
|
||||
/>
|
||||
</view>
|
||||
</wd-cell>
|
||||
|
||||
<!-- 多行文本 -->
|
||||
<wd-textarea
|
||||
v-else-if="['textarea', 'markdown', 'umeditor'].includes(item.type)"
|
||||
:label-width="labelWidth"
|
||||
:label="get4Label(item.label)"
|
||||
:name="item.key"
|
||||
v-model="formData[item.key]"
|
||||
clearable
|
||||
:maxlength="300"
|
||||
:disabled="componentDisabled(item)"
|
||||
:placeholder="item.placeholder"
|
||||
:rules="item.rules"
|
||||
/>
|
||||
<!-- 密码输入框 -->
|
||||
<wd-input
|
||||
v-else-if="item.type === 'password'"
|
||||
:label-width="labelWidth"
|
||||
v-model="formData[item.key]"
|
||||
:disabled="componentDisabled(item)"
|
||||
:label="get4Label(item.label)"
|
||||
:name="item.key"
|
||||
:placeholder="item.placeholder"
|
||||
:rules="item.rules"
|
||||
show-password
|
||||
/>
|
||||
<!-- popup字典 -->
|
||||
<PopupDict
|
||||
v-else-if="item.type === 'popup_dict'"
|
||||
:label-width="labelWidth"
|
||||
:label="get4Label(item.label)"
|
||||
:disabled="componentDisabled(item)"
|
||||
:required="fieldRequired(item)"
|
||||
v-model="formData[item.key]"
|
||||
:multi="item.formSchema.popupMulti"
|
||||
:dictCode="`${item.formSchema.code},${item.formSchema['destFields']},${item.formSchema['orgFields']}`"
|
||||
></PopupDict>
|
||||
<!-- popup -->
|
||||
<Popup
|
||||
v-else-if="item.type === 'popup'"
|
||||
:label-width="labelWidth"
|
||||
:label="get4Label(item.label)"
|
||||
:disabled="componentDisabled(item)"
|
||||
:required="fieldRequired(item)"
|
||||
v-model="formData[item.key]"
|
||||
:multi="item.formSchema.popupMulti"
|
||||
:code="`${item.formSchema.code}`"
|
||||
:setFieldsValue="setFieldsValue"
|
||||
:fieldConfig="getPopupFieldConfig(item)"
|
||||
></Popup>
|
||||
<!-- 关联记录 -->
|
||||
<online-popup-link-record
|
||||
v-else-if="item.type === 'link_table'"
|
||||
:label-width="labelWidth"
|
||||
:label="get4Label(item.label)"
|
||||
:name="item.key"
|
||||
v-model:formSchema="item.formSchema"
|
||||
:disabled="componentDisabled(item)"
|
||||
:required="fieldRequired(item)"
|
||||
v-model:value="formData[item.key]"
|
||||
@selected="linkRecordChange"
|
||||
></online-popup-link-record>
|
||||
<!-- 他表字段 -->
|
||||
<wd-input
|
||||
v-else-if="item.type === 'link_table_field'"
|
||||
:label-width="labelWidth"
|
||||
v-model="formData[item.key]"
|
||||
:disabled="true"
|
||||
:label="get4Label(item.label)"
|
||||
:name="item.key"
|
||||
/>
|
||||
<!-- 用户选择 -->
|
||||
<select-user
|
||||
v-else-if="item.type === 'sel_user'"
|
||||
:label-width="labelWidth"
|
||||
:name="item.key"
|
||||
:label="get4Label(item.label)"
|
||||
:disabled="componentDisabled(item)"
|
||||
:required="fieldRequired(item)"
|
||||
v-model="formData[item.key]"
|
||||
></select-user>
|
||||
|
||||
<!-- 部门选择 -->
|
||||
<select-dept
|
||||
v-else-if="item.type === 'sel_depart'"
|
||||
:label-width="labelWidth"
|
||||
:name="item.key"
|
||||
:label="get4Label(item.label)"
|
||||
labelKey="departName"
|
||||
:disabled="componentDisabled(item)"
|
||||
:required="fieldRequired(item)"
|
||||
v-model="formData[item.key]"
|
||||
></select-dept>
|
||||
<!-- 分类字典树 -->
|
||||
<CategorySelect
|
||||
v-else-if="item.type === 'cat_tree'"
|
||||
:label-width="labelWidth"
|
||||
:label="get4Label(item.label)"
|
||||
:disabled="componentDisabled(item)"
|
||||
:required="fieldRequired(item)"
|
||||
v-model="formData[item.key]"
|
||||
:pid="`${item.formSchema.pidValue}`"
|
||||
></CategorySelect>
|
||||
<!-- 自定义树 -->
|
||||
<TreeSelect
|
||||
v-else-if="item.type === 'sel_tree'"
|
||||
:label-width="labelWidth"
|
||||
:label="get4Label(item.label)"
|
||||
:disabled="componentDisabled(item)"
|
||||
:required="fieldRequired(item)"
|
||||
v-model="formData[item.key]"
|
||||
:dict="`${item.formSchema.dict}`"
|
||||
:pidField="`${item.formSchema.pidField}`"
|
||||
:pidValue="`${item.formSchema.pidValue}`"
|
||||
:hasChildField="`${item.formSchema.hasChildField}`"
|
||||
></TreeSelect>
|
||||
<!-- 普通输入框 -->
|
||||
<wd-input
|
||||
v-else
|
||||
:label-width="labelWidth"
|
||||
v-model="formData[item.key]"
|
||||
:disabled="componentDisabled(item)"
|
||||
:label="get4Label(item.label)"
|
||||
:name="item.key"
|
||||
:placeholder="item.placeholder"
|
||||
:rules="item.rules"
|
||||
clearable
|
||||
/>
|
||||
</view>
|
||||
</wd-cell-group>
|
||||
</wd-form>
|
||||
</view>
|
||||
<view class="footer" v-if="showFooter">
|
||||
<wd-button :disabled="loading" block :loading="loading" @click="formSubmit">提交</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import FormProperty from '@/components/online/FormProperty'
|
||||
import OnlineImage from '@/components/online/view/online-image.vue'
|
||||
import OnlineSelect from '@/components/online/view/online-select.vue'
|
||||
import OnlineTime from '@/components/online/view/online-time.vue'
|
||||
import OnlineDatetime from '@/components/online/view/online-datetime.vue'
|
||||
import OnlineDate from '@/components/online/view/online-date.vue'
|
||||
import OnlineRadio from '@/components/online/view/online-radio.vue'
|
||||
import OnlineCheckbox from '@/components/online/view/online-checkbox.vue'
|
||||
import OnlineMulti from '@/components/online/view/online-multi.vue'
|
||||
import OnlinePca from '@/components/online/view/online-pca.vue'
|
||||
import OnlinePopupLinkRecord from '@/components/online/view/online-popup-link-record.vue'
|
||||
import SelectDept from '@/components/SelectDept/SelectDept.vue'
|
||||
import SelectUser from '@/components/SelectUser/SelectUser.vue'
|
||||
import { loadOneFieldDefVal } from './defaultVal'
|
||||
import { useToast, useMessage, useNotify } from 'wot-design-uni'
|
||||
import { http } from '@/utils/http'
|
||||
import { deepClone } from 'wot-design-uni/components/common/util'
|
||||
import { isArray, isNumber } from '@/utils/is'
|
||||
import { formatDate } from '@/common/uitls'
|
||||
import { duplicateCheck } from '@/service/api'
|
||||
|
||||
// 接收 props
|
||||
const props = defineProps({
|
||||
showHeader: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
table: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
taskId: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
showFooter: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
edit: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
flowEdit: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
dataId: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
onlyBackData: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
// 定义 emits
|
||||
const emits = defineEmits(['back', 'success', 'formSuccess'])
|
||||
const toast = useToast()
|
||||
// 定义响应式数据
|
||||
const onlinekey = ref(1)
|
||||
//刷新状态
|
||||
const reFresh = ref(false)
|
||||
//请求地址
|
||||
const url = ref({
|
||||
load: '/online/cgform/api/getFormItemBytbname/',
|
||||
optPre: '/online/cgform/api/form/',
|
||||
})
|
||||
// 表单ID
|
||||
const code = ref('')
|
||||
//表类型,1:单表,2:一对多
|
||||
const single = ref(false)
|
||||
//是否树表
|
||||
const treeForm = ref(false)
|
||||
//表描述
|
||||
const tableTxt = ref('')
|
||||
//表名称
|
||||
const tableName = ref('')
|
||||
//表名称
|
||||
const tableType = ref(1)
|
||||
//表数据Data
|
||||
const formData = ref<any>({})
|
||||
//是否填值规则字段
|
||||
const hasFillRuleFields = ref('')
|
||||
//是否必填字段
|
||||
const hasRequiredFields = ref([])
|
||||
//字段属性
|
||||
const rootProperties = ref<any>([])
|
||||
//小程序字段属性
|
||||
const wxProperties = ref<any>([])
|
||||
//加载状态
|
||||
const loading = ref(false)
|
||||
//表单数据ID
|
||||
const formDataId = ref('')
|
||||
|
||||
// 导航标题
|
||||
const navTitle = computed(() => {
|
||||
if (!props.title || props.title.length === 0) {
|
||||
return tableTxt.value
|
||||
} else {
|
||||
return props.title
|
||||
}
|
||||
})
|
||||
// 标题宽度
|
||||
const labelWidth = computed(() => {
|
||||
return '100px'
|
||||
})
|
||||
// 导航标题
|
||||
const get4Label = computed(() => {
|
||||
return (lable) => {
|
||||
return `${lable && lable.length > 4 ? lable.substring(0, 4) : lable}:`
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
console.log('开始渲染online表单')
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取日期控件的扩展类型
|
||||
* @param formSchema
|
||||
* @returns {string}
|
||||
*/
|
||||
const getDateExtendType = (formSchema: any) => {
|
||||
if (formSchema.fieldExtendJson) {
|
||||
let fieldExtendJson = JSON.parse(formSchema.fieldExtendJson)
|
||||
let mapField = {
|
||||
month: 'year-month',
|
||||
year: 'year',
|
||||
quarter: 'date',
|
||||
week: 'date',
|
||||
day: 'date',
|
||||
}
|
||||
return fieldExtendJson?.picker && mapField[fieldExtendJson?.picker]
|
||||
? mapField[fieldExtendJson?.picker]
|
||||
: 'date'
|
||||
}
|
||||
return 'date'
|
||||
}
|
||||
/**
|
||||
* 判断是否选中
|
||||
* @param opts
|
||||
* @param value
|
||||
* @returns {boolean|boolean}
|
||||
*/
|
||||
const isChecked = (opts: any, value: any) => {
|
||||
return opts && opts.length > 0 ? value === opts[0] : false
|
||||
}
|
||||
/**
|
||||
* 开关选项
|
||||
* @param opts
|
||||
* @param value
|
||||
* @returns {boolean|boolean}
|
||||
*/
|
||||
const switchOpt = (opts: any, index: any) => {
|
||||
const options = Array.isArray(opts) && opts.length > 0 ? opts : ['Y', 'N']
|
||||
return options[index] + ''
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param item
|
||||
* @returns {*|boolean}
|
||||
*/
|
||||
const componentDisabled = (item: any) => {
|
||||
if (props.disabled === true || (!props.showFooter && !props.onlyBackData)) {
|
||||
return true
|
||||
}
|
||||
return item.disabled
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字段是否必填
|
||||
* @param item
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const fieldRequired = (item: any) => {
|
||||
return item?.key && hasRequiredFields.value.includes(item.key)
|
||||
}
|
||||
/**
|
||||
* 关联记录同步修改他表字段
|
||||
* @param linkRecord
|
||||
* @param key
|
||||
*/
|
||||
const linkRecordChange = (linkRecord, key) => {
|
||||
let linkFieldArr = rootProperties.value.filter(
|
||||
(item) => item.type === 'link_table_field' && item?.formSchema?.dictTable == key,
|
||||
)
|
||||
linkFieldArr.forEach((field) => {
|
||||
let value = linkRecord.map((record) => record[field.formSchema.dictText]).join(',')
|
||||
nextTick(() => {
|
||||
formData.value[field.key] = value
|
||||
})
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 返回按钮点击事件
|
||||
*/
|
||||
const backRoute = () => {
|
||||
emits('back')
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理多选字段
|
||||
* @param value
|
||||
*/
|
||||
function handleMultiOrDateField() {
|
||||
let finalData = deepClone(formData.value)
|
||||
//日期字段
|
||||
let dateFieldArr = rootProperties.value.filter(
|
||||
(item) => item.type === 'date' || item.type === 'datetime',
|
||||
)
|
||||
//省市区字段
|
||||
let pcaArr = rootProperties.value.filter((item) => item.type === 'pca')
|
||||
finalData = Object.keys(finalData).reduce((acc, key) => {
|
||||
let value = finalData[key]
|
||||
//省市区获取最后一位
|
||||
if (value && pcaArr.length > 0 && pcaArr.map((item) => item.key).includes(key)) {
|
||||
console.log('省市区获取最后一位value', value)
|
||||
value = isArray(value) ? value[2] : value.split(',')[2]
|
||||
}
|
||||
//是数组的就转换成字符串
|
||||
if (value && isArray(value)) {
|
||||
value = value.join(',')
|
||||
}
|
||||
//时间戳类型的日期转具体格式字符串
|
||||
if (dateFieldArr.length > 0) {
|
||||
const dateField = dateFieldArr.find((obj) => obj.key === key)
|
||||
if (dateField) {
|
||||
value =
|
||||
value && isNumber(value)
|
||||
? formatDate(
|
||||
value,
|
||||
dateField.type === 'datetime' ? 'yyyy-MM-dd HH:mm:ss' : 'yyyy-MM-dd',
|
||||
)
|
||||
: value
|
||||
}
|
||||
}
|
||||
acc[key] = value
|
||||
return acc
|
||||
}, {})
|
||||
return finalData
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单提交事件
|
||||
* @param e
|
||||
*/
|
||||
const formSubmit = async (e) => {
|
||||
// 判断字段必填和正则
|
||||
if (await fieldCheck(formData.value)) {
|
||||
return
|
||||
}
|
||||
// 处理多选字段
|
||||
let finalData = await handleMultiOrDateField()
|
||||
// 判断是否只返回数据
|
||||
if (props.onlyBackData) {
|
||||
emits('success', finalData)
|
||||
return
|
||||
}
|
||||
if (props.flowEdit === true) {
|
||||
// 原代码未实现该逻辑,可按需补充
|
||||
} else if (props.edit === true) {
|
||||
//表单编辑
|
||||
await handleEdit(finalData)
|
||||
} else {
|
||||
//表单新增
|
||||
await handleSave(finalData)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 校验字段
|
||||
* @param values
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const fieldCheck = async (values: any) => {
|
||||
// 校验字段
|
||||
let flag = false
|
||||
for (const item of rootProperties.value) {
|
||||
// 校验提示
|
||||
const tip = (msg) => {
|
||||
// 提示校验未通过
|
||||
toast.warning(msg)
|
||||
flag = true
|
||||
}
|
||||
// 校验必填
|
||||
if (fieldRequired(item) && !values[item.key]) {
|
||||
tip(`${item.label}不能为空!`)
|
||||
break
|
||||
}
|
||||
// 校验正则
|
||||
let pattern = item?.formSchema?.pattern
|
||||
if (pattern) {
|
||||
if (pattern == 'only') {
|
||||
const res: any = await duplicateCheck({
|
||||
tableName: tableName.value,
|
||||
fieldName: item.key,
|
||||
fieldVal: values[item.key],
|
||||
dataId: formDataId.value,
|
||||
})
|
||||
if (!res.success) {
|
||||
tip(`${item.label} ${res.message}`)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
const regex = new RegExp(pattern)
|
||||
if (values[item.key] && pattern && !regex.test(values[item.key])) {
|
||||
let errorInfo = item?.formSchema?.errorInfo || '格式不正确!'
|
||||
tip(`${item.label}${errorInfo}`)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return flag
|
||||
}
|
||||
/**
|
||||
* 新增保存
|
||||
*/
|
||||
const handleSave = (finalData: any) => {
|
||||
if (finalData?.bpm_status) {
|
||||
finalData.bpm_status = '1'
|
||||
}
|
||||
console.log('===onlineForm表单组件走新增保存 handleSave===', finalData)
|
||||
http
|
||||
.post(`${url.value.optPre}${code.value}?tabletype=${tableType.value}`, finalData)
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
emits('success', res.result)
|
||||
} else {
|
||||
toast.warning(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 编辑保存
|
||||
*/
|
||||
const handleEdit = (finalData: any) => {
|
||||
http
|
||||
.put(`${url.value.optPre}${code.value}?tabletype=${tableType.value}`, finalData)
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
emits('success', formData.value.id)
|
||||
} else {
|
||||
toast.warning(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 根据表名加载表单数据
|
||||
* @param dataID
|
||||
*/
|
||||
const loadByTableName = (dataID: any) => {
|
||||
formDataId.value = props.dataId
|
||||
// #ifdef MP-WEIXIN
|
||||
if (dataID && dataID.length > 0) {
|
||||
formDataId.value = dataID
|
||||
}
|
||||
// #endif
|
||||
let urlStr = url.value.load + props.table
|
||||
http.get(urlStr, { taskId: props.taskId }).then((res: any) => {
|
||||
if (res.success) {
|
||||
console.log('===onlineForm加载表单数据 schema===', res.result.schema)
|
||||
let config = res.result
|
||||
code.value = config.head.id
|
||||
single.value = config.head.tableType === 1
|
||||
tableType.value = config.head.tableType
|
||||
createRootProperties(config.schema)
|
||||
treeForm.value = config.head.isTree === 'Y'
|
||||
tableTxt.value = config.head.tableTxt
|
||||
if (props.edit === true || props.flowEdit === true) {
|
||||
loadFormData()
|
||||
} else {
|
||||
// 新增页面处理表单默认值
|
||||
handleDefaultValue()
|
||||
}
|
||||
} else {
|
||||
// 假设 $tip 是全局方法,可按需修改
|
||||
toast.info(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 创建根属性
|
||||
* @param formSchema
|
||||
*/
|
||||
const createRootProperties = (formSchema: any) => {
|
||||
tableName.value = formSchema.table
|
||||
formData.value = {}
|
||||
hasFillRuleFields.value = formSchema.hasFillRuleFields
|
||||
hasRequiredFields.value = formSchema?.required ?? []
|
||||
const properties = formSchema.properties
|
||||
let rootProps = [],
|
||||
subInfo = []
|
||||
console.log('===onlineForm表单配置项 properties===', properties)
|
||||
Object.keys(properties).map((key) => {
|
||||
if (key) {
|
||||
const item = properties[key]
|
||||
//TODO 配置主子表的
|
||||
if (item.view === 'tab') {
|
||||
subInfo.push(item)
|
||||
} else {
|
||||
formData.value[key] = ''
|
||||
let fp = FormProperty(key, item, formSchema.required)
|
||||
rootProps.push(fp)
|
||||
}
|
||||
}
|
||||
})
|
||||
rootProps.sort((one, next) => {
|
||||
return one.formSchema.order - next.formSchema.order
|
||||
})
|
||||
rootProperties.value = [...rootProps]
|
||||
console.log('--rootProperties--', rootProps)
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// wxProperties.value = rootProperties.value.map((item) => {
|
||||
// let {
|
||||
// popupCode,
|
||||
// formSchema,
|
||||
// dest,
|
||||
// ogn,
|
||||
// key,
|
||||
// label,
|
||||
// placeholder,
|
||||
// type,
|
||||
// rules,
|
||||
// listSource,
|
||||
// dictStr,
|
||||
// disabled,
|
||||
// } = item
|
||||
// let event = {
|
||||
// formSchema,
|
||||
// dest,
|
||||
// key,
|
||||
// type,
|
||||
// rules,
|
||||
// listSource,
|
||||
// label,
|
||||
// dictStr,
|
||||
// disabled,
|
||||
// placeholder,
|
||||
// ogn,
|
||||
// popupCode,
|
||||
// }
|
||||
// return event
|
||||
// })
|
||||
// console.log('this.wxProperties', wxProperties.value)
|
||||
|
||||
nextTick(() => {
|
||||
reFresh.value = true
|
||||
onlinekey.value += 1
|
||||
})
|
||||
// #endif
|
||||
emits('formSuccess', true)
|
||||
}
|
||||
/**
|
||||
* 获取字段类型
|
||||
* @param item
|
||||
* @returns {string}
|
||||
*/
|
||||
const getFieldNumberType = (item: any) => {
|
||||
return item.onlyInteger === true ? 'digit' : 'number'
|
||||
}
|
||||
/**
|
||||
* 获取表单数据
|
||||
*/
|
||||
const loadFormData = () => {
|
||||
let urlStr = url.value.optPre + code.value + '/' + formDataId.value
|
||||
urlStr = urlStr.replace(/"/g, '')
|
||||
http.get(urlStr).then((res: any) => {
|
||||
if (res.success) {
|
||||
formData.value = { ...res.result }
|
||||
console.log('===onlineForm表单组件走获取表单数据 loadFormData===', formData.value)
|
||||
// #ifdef MP-WEIXIN
|
||||
reFresh.value = false
|
||||
nextTick(() => {
|
||||
reFresh.value = true
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 获取默认值
|
||||
*/
|
||||
const handleDefaultValue = () => {
|
||||
console.log('===onlineForm表单组件走默认值 handleDefaultValue===')
|
||||
rootProperties.value.forEach((item) => {
|
||||
let field = item.key
|
||||
let { defVal, type } = item.formSchema
|
||||
loadOneFieldDefVal(defVal, type, (value) => {
|
||||
formData.value[field] = value
|
||||
})
|
||||
})
|
||||
}
|
||||
const setFieldsValue = (data) => {
|
||||
formData.value = { ...formData.value, ...data }
|
||||
}
|
||||
const getPopupFieldConfig = (item) => {
|
||||
const { formSchema } = item
|
||||
const { destFields = '', orgFields = '' } = formSchema
|
||||
const result = orgFields.split(',').map((oField, index) => {
|
||||
return {
|
||||
source: oField,
|
||||
target: destFields.split(',')[index],
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
defineExpose({
|
||||
navTitle,
|
||||
getDateExtendType,
|
||||
isChecked,
|
||||
componentDisabled,
|
||||
fieldRequired,
|
||||
backRoute,
|
||||
formSubmit,
|
||||
loadByTableName,
|
||||
getFieldNumberType,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.onlineLoader-container {
|
||||
.form-container {
|
||||
:deep(.wd-cell-group__body){
|
||||
background: #F1F1F1;
|
||||
}
|
||||
.onlineLoader-form{
|
||||
:deep(.wd-input__label-inner){
|
||||
font-size: 16px;
|
||||
}
|
||||
:deep(.wd-picker__label){
|
||||
font-size: 16px;
|
||||
}
|
||||
:deep(.wd-select-picker__label){
|
||||
font-size: 16px;
|
||||
}
|
||||
:deep(.wd-cell__title){
|
||||
font-size: 16px;
|
||||
}
|
||||
:deep(.wd-textarea__label-inner){
|
||||
font-size: 16px;
|
||||
}
|
||||
:deep(.wd-input__label.is-required){
|
||||
padding-left: 0px;
|
||||
}
|
||||
:deep(.wd-input__label.is-required::after){
|
||||
left: -10px;
|
||||
}
|
||||
:deep(.wd-textarea__clear){
|
||||
color: #bfbfbf;
|
||||
}
|
||||
:deep(.wd-select-picker__clear){
|
||||
color: #bfbfbf;
|
||||
}
|
||||
:deep(.wd-input__clear){
|
||||
color: #bfbfbf;
|
||||
}
|
||||
:deep(.wd-upload__close){
|
||||
color: #bfbfbf;
|
||||
}
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
303
src/components/online/view/link-records-modal.vue
Normal file
303
src/components/online/view/link-records-modal.vue
Normal file
@ -0,0 +1,303 @@
|
||||
<template>
|
||||
<wd-popup position="bottom" v-model="show">
|
||||
<PageLayout
|
||||
:navTitle="navTitle"
|
||||
type="popup"
|
||||
navRightText="确定"
|
||||
@navRight="handleConfirm"
|
||||
@navBack="handleCancel"
|
||||
>
|
||||
<z-paging
|
||||
ref="paging"
|
||||
:fixed="false"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
:default-page-size="15"
|
||||
>
|
||||
<!-- <template #top>-->
|
||||
<!-- <wd-search-->
|
||||
<!-- hide-cancel-->
|
||||
<!-- :placeholder="search.placeholder"-->
|
||||
<!-- v-model="search.keyword"-->
|
||||
<!-- @search="handleSearch"-->
|
||||
<!-- @clear="handleClear"-->
|
||||
<!-- />-->
|
||||
<!-- </template>-->
|
||||
<template v-if="multi">
|
||||
<wd-checkbox-group shape="square" v-model="checkedValue">
|
||||
<template v-for="(item, index) in dataList" :key="index">
|
||||
<view class="list" @click="hanldeCheck(index)">
|
||||
<view class="left text-gray-5">
|
||||
<view class="cu-avatar lg mr-4" v-if="imageField && item[imageField]" :style="[{backgroundImage:'url('+ (item[imageField]) +')'}]"></view>
|
||||
<view class="field-content">
|
||||
<template v-for="(cItem, cIndex) in columns" :key="cIndex">
|
||||
<view class="row">
|
||||
<text class="label">{{ cItem.title }}:</text>
|
||||
<text class="value">{{ item[cItem.dataIndex] }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
<view class="right" @click.stop>
|
||||
<wd-checkbox ref="checkboxRef" :modelValue="index"></wd-checkbox>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</wd-checkbox-group>
|
||||
</template>
|
||||
<template v-else>
|
||||
<wd-radio-group shape="dot" v-model="checkedValue">
|
||||
<template v-for="(item, index) in dataList" :key="index">
|
||||
<wd-cell>
|
||||
<view class="list" @click="hanldeCheck(index)">
|
||||
<view class="left text-gray-5">
|
||||
<view class="cu-avatar lg mr-4" v-if="imageField && item[imageField]" :style="[{backgroundImage:'url('+ (item[imageField]) +')'}]"></view>
|
||||
<view class="field-content">
|
||||
<template v-for="(cItem, cIndex) in columns" :key="cIndex">
|
||||
<view class="row">
|
||||
<text class="label">{{ cItem.title }}:</text>
|
||||
<text class="value">{{ item[cItem.dataIndex] }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
<view class="right" @click.stop>
|
||||
<wd-radio :value="index"></wd-radio>
|
||||
</view>
|
||||
</view>
|
||||
</wd-cell>
|
||||
</template>
|
||||
</wd-radio-group>
|
||||
</template>
|
||||
</z-paging>
|
||||
</PageLayout>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { http } from '@/utils/http'
|
||||
import { isArray } from '@/utils/is'
|
||||
import { getFileAccessHttpUrl } from '@/common/uitls'
|
||||
defineOptions({
|
||||
name: 'popupReportModal',
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const props = defineProps({
|
||||
dictTable:{
|
||||
type: String,
|
||||
required:true,
|
||||
},
|
||||
dictCode:{
|
||||
type:String,
|
||||
required:true,
|
||||
},
|
||||
dictText:{
|
||||
type: String,
|
||||
required:true,
|
||||
},
|
||||
multi:{
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
imageField:{
|
||||
type: String,
|
||||
required:false,
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['change', 'close'])
|
||||
const toast = useToast();
|
||||
const show = ref(true);
|
||||
const api = {
|
||||
getColumns: '/online/cgform/api/getColumns',
|
||||
getData: '/online/cgform/api/getData'
|
||||
}
|
||||
console.log('props:::', props)
|
||||
const navTitle = ref('');
|
||||
const paging = ref(null);
|
||||
const dataList = ref([]);
|
||||
// 报表id
|
||||
let rpConfigId = null;
|
||||
let loadedColumns = false;
|
||||
const columns = ref([]);
|
||||
const selectArr = ref([]);
|
||||
const checkedValue: any = ref(props.multi ? [] : '')
|
||||
const checkboxRef = ref(null)
|
||||
const search = reactive({
|
||||
keyword: '',
|
||||
placeholder: '',
|
||||
field: '',
|
||||
})
|
||||
|
||||
const handleClose = () => {
|
||||
setTimeout(() => {
|
||||
emit('close')
|
||||
}, 400)
|
||||
}
|
||||
|
||||
const beforeOpen = (arr) => {
|
||||
selectArr.value = arr || []
|
||||
}
|
||||
const handleConfirm = () => {
|
||||
if (checkedValue.value.length == 0) {
|
||||
toast.warning('还没选择~')
|
||||
return
|
||||
}
|
||||
const result = []
|
||||
let value = checkedValue.value
|
||||
if (!Array.isArray(checkedValue.value)) {
|
||||
value = [checkedValue.value]
|
||||
}
|
||||
value.forEach((index) => {
|
||||
result.push(dataList.value[index])
|
||||
})
|
||||
show.value = false
|
||||
emit('change', result)
|
||||
handleClose()
|
||||
}
|
||||
const handleCancel = () => {
|
||||
show.value = false
|
||||
handleClose()
|
||||
console.log('取消了~')
|
||||
}
|
||||
// 搜索
|
||||
function handleSearch() {
|
||||
paging.value.reload()
|
||||
}
|
||||
// 清除搜索条件
|
||||
function handleClear() {
|
||||
search.keyword = ''
|
||||
handleSearch()
|
||||
}
|
||||
const hanldeCheck = (index) => {
|
||||
if (props.multi) {
|
||||
if (Array.isArray(checkboxRef.value)) {
|
||||
checkboxRef.value[index].toggle()
|
||||
}
|
||||
} else {
|
||||
checkedValue.value = index
|
||||
}
|
||||
}
|
||||
|
||||
const getRpColumns = () => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (loadedColumns) {
|
||||
resolve()
|
||||
} else {
|
||||
let linkTableSelectFields = props.dictCode + ',' + props.dictText;
|
||||
http
|
||||
.get(`${api.getColumns}/${props.dictTable}?linkTableSelectFields=${linkTableSelectFields}`)
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
loadedColumns = true
|
||||
const { result } = res
|
||||
navTitle.value = result.description
|
||||
rpConfigId = result.code
|
||||
result.columns?.forEach((item) => {
|
||||
if (linkTableSelectFields.includes(item.dataIndex)) {
|
||||
columns.value.push(item)
|
||||
}
|
||||
})
|
||||
resolve()
|
||||
} else {
|
||||
reject()
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
reject()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const queryList = (pageNo, pageSize) => {
|
||||
const pararms = { pageNo, pageSize,linkTableSelectFields:"" }
|
||||
if (search.keyword) {
|
||||
pararms[search.field] = `*${search.keyword}*`
|
||||
}
|
||||
getRpColumns()
|
||||
.then(() => {
|
||||
let linkTableSelectFields = props.dictCode + ',' + props.dictText;
|
||||
if(props.imageField){
|
||||
linkTableSelectFields = linkTableSelectFields + ',' + props.imageField;
|
||||
}
|
||||
pararms.linkTableSelectFields = linkTableSelectFields;
|
||||
http
|
||||
.get(`${api.getData}/${props.dictTable}`, pararms)
|
||||
.then((res: any) => {
|
||||
if (res.success && res.result.records) {
|
||||
let dataRecords = res.result.records;
|
||||
if(dataRecords && dataRecords.length>0){
|
||||
let id = dataRecords[0]['id'];
|
||||
for(let item of dataRecords){
|
||||
if(!id){
|
||||
item.id = new Date().getTime();
|
||||
}
|
||||
if(props.imageField && item[props.imageField]){
|
||||
let imgUrlArr = item[props.imageField].split(",");
|
||||
item[props.imageField] = imgUrlArr.length>0?getFileAccessHttpUrl(imgUrlArr[0]):"";
|
||||
}
|
||||
}
|
||||
}
|
||||
//TODO
|
||||
if(selectArr.value && isArray(selectArr) && selectArr.length>0){
|
||||
//checkedValue.value = [...selectArr]
|
||||
}
|
||||
paging.value.complete(dataRecords ?? [])
|
||||
} else {
|
||||
paging.value.complete(false)
|
||||
}
|
||||
})
|
||||
.catch((err) => {})
|
||||
})
|
||||
.catch((err) => {})
|
||||
}
|
||||
defineExpose({
|
||||
beforeOpen
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.wd-cell) {
|
||||
--wot-color-white: tranparent;
|
||||
--wot-cell-padding: 0;
|
||||
.wd-cell__wrapper {
|
||||
--wot-cell-wrapper-padding: 0;
|
||||
}
|
||||
.wd-cell__left {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
:deep(.wd-checkbox-group) {
|
||||
--wot-checkbox-bg: tranparent;
|
||||
}
|
||||
:deep(.wd-radio-group) {
|
||||
--wot-radio-bg: tranparent;
|
||||
}
|
||||
.list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #fff;
|
||||
padding: 16px;
|
||||
margin-top: 16px;
|
||||
.left {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.field-content{
|
||||
.row {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
:deep(.wd-checkbox) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
138
src/components/online/view/online-checkbox.vue
Normal file
138
src/components/online/view/online-checkbox.vue
Normal file
@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<wd-select-picker
|
||||
:label-width="labelWidth"
|
||||
:label="label"
|
||||
v-model="selected"
|
||||
filterable
|
||||
clearable
|
||||
:columns="options"
|
||||
:disabled="disabled"
|
||||
placeholder="请选择"
|
||||
@change="handleChange"
|
||||
></wd-select-picker>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from 'vue'
|
||||
import { isArray, isString } from 'lodash'
|
||||
import {http} from "@/utils/http"; // 假设使用 lodash 来判断类型
|
||||
|
||||
// 定义 props
|
||||
const props = defineProps({
|
||||
dict: {
|
||||
type: [Array, String],
|
||||
default: () => [],
|
||||
required: true,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
labelWidth: {
|
||||
type: String,
|
||||
default: '80px',
|
||||
required: false,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
dictStr: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
value: {
|
||||
type: [Array, String],
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
// 定义 emits
|
||||
const emit = defineEmits(['input', 'change', 'update:value'])
|
||||
|
||||
// 定义响应式数据
|
||||
const selected = ref([]);
|
||||
const options = ref([]);
|
||||
|
||||
// 初始化选项
|
||||
const initSelections = async () => {
|
||||
options.value = []
|
||||
if (props.type === 'sel_search' && props.dictStr) {
|
||||
let temp = props.dictStr
|
||||
if (temp.indexOf(' ') > 0) {
|
||||
temp = encodeURI(props.dictStr)
|
||||
}
|
||||
try {
|
||||
const res = await http.get('/sys/dict/getDictItems/' + temp)
|
||||
if (res.success) {
|
||||
options.value = res.result
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('请求数据出错:', error)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!props.dict || props.dict.length === 0) {
|
||||
return
|
||||
}
|
||||
if (isString(props.dict)) {
|
||||
try {
|
||||
const res = await http.get('/sys/dict/getDictItems/' + props.dict)
|
||||
if (res.success) {
|
||||
options.value = res.result
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('请求数据出错:', error)
|
||||
}
|
||||
} else {
|
||||
props.dict.forEach((item) => {
|
||||
options.value.push(item)
|
||||
})
|
||||
}
|
||||
}
|
||||
console.log("options.value ",options.value )
|
||||
}
|
||||
|
||||
// 选择器改变事件处理函数
|
||||
const handleChange = (e) => {
|
||||
let value = "";
|
||||
if (selected.value && isArray(selected.value)) {
|
||||
value = selected.value.join(',')
|
||||
}
|
||||
emit('update:value', value);
|
||||
emit('change', value);
|
||||
}
|
||||
|
||||
// 监听 dict 和 value 的变化
|
||||
watch(() => props.dict, () => {
|
||||
initSelections();
|
||||
});
|
||||
// 监听 value 的变化
|
||||
watch(
|
||||
() => props.value,
|
||||
(val) => {
|
||||
selected.value = !val? [] : val.split(',');
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
// 组件挂载时初始化选项
|
||||
onMounted(() => {
|
||||
initSelections()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
72
src/components/online/view/online-date.vue
Normal file
72
src/components/online/view/online-date.vue
Normal file
@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<wd-datetime-picker
|
||||
:disabled="disabled"
|
||||
:type="type"
|
||||
:labelWidth="labelWidth"
|
||||
v-model="currentTime"
|
||||
:label="label"
|
||||
@confirm="handleConfirm"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 定义 props
|
||||
import { isString } from '@/utils/is'
|
||||
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
labelWidth: {
|
||||
type: String,
|
||||
default: '80px',
|
||||
required: false,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'date',
|
||||
required: false,
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
// 定义 emits
|
||||
const emit = defineEmits(['input', 'change', 'update:value'])
|
||||
// 定义响应式数据;
|
||||
const visible = ref(false)
|
||||
const currentTime = ref('')
|
||||
|
||||
// 监听 value 的变化
|
||||
watch(
|
||||
() => props.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
currentTime.value = val && isString(val) ? new Date(val).getTime() : val
|
||||
} else {
|
||||
currentTime.value = ''
|
||||
}
|
||||
},
|
||||
)
|
||||
// 选择器改变事件处理函数
|
||||
const handleConfirm = (e) => {
|
||||
emit('update:value', currentTime.value)
|
||||
emit('change', currentTime.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
73
src/components/online/view/online-datetime.vue
Normal file
73
src/components/online/view/online-datetime.vue
Normal file
@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<wd-datetime-picker
|
||||
:disabled="disabled"
|
||||
:labelWidth="labelWidth"
|
||||
v-model="currentTime"
|
||||
:label="label"
|
||||
@confirm="handleConfirm"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 定义 props
|
||||
import dayjs from 'dayjs'
|
||||
import { isString } from '@/utils/is'
|
||||
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
labelWidth: {
|
||||
type: String,
|
||||
default: '80px',
|
||||
required: false,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
// 定义 emits
|
||||
const emit = defineEmits(['input', 'change', 'update:value'])
|
||||
// 定义响应式数据;
|
||||
const visible = ref(false)
|
||||
const currentTime = ref('')
|
||||
|
||||
// 监听 value 的变化
|
||||
watch(
|
||||
() => props.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
console.log("日期时间变化val",val);
|
||||
currentTime.value = val && isString(val) ? new Date(val).getTime() : val
|
||||
} else {
|
||||
currentTime.value = ''
|
||||
}
|
||||
},
|
||||
)
|
||||
// 选择器改变事件处理函数
|
||||
const handleConfirm = (e) => {
|
||||
emit('update:value', currentTime.value)
|
||||
emit('change', currentTime.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
165
src/components/online/view/online-image.vue
Normal file
165
src/components/online/view/online-image.vue
Normal file
@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<wd-upload
|
||||
v-model:file-list="fileList"
|
||||
:accept="uploadFileType"
|
||||
:upload-method="customUpload"
|
||||
:disabled="disabled"
|
||||
:before-remove="delFile"
|
||||
></wd-upload>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { UploadMethod } from '@/uni_modules/wot-design-uni/components/wd-upload/types'
|
||||
import { getEnvBaseUploadUrl } from '@/utils'
|
||||
import { useUserStore } from '@/store'
|
||||
import { getFileAccessHttpUrl } from '@/common/uitls'
|
||||
import {isString} from "@/utils/is";
|
||||
import {useToast} from "wot-design-uni";
|
||||
const toast = useToast()
|
||||
const VITE_UPLOAD_BASEURL = `${getEnvBaseUploadUrl()}`
|
||||
// 接收 props
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
uploadFileType: {
|
||||
type: String,
|
||||
default: 'image',
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
// 定义 emits
|
||||
const emit = defineEmits(['change', 'update:value'])
|
||||
// 定义响应式数据
|
||||
const fileList = ref([])
|
||||
/**
|
||||
* 自定义上传方法
|
||||
* @param file
|
||||
* @param formData
|
||||
* @param options
|
||||
*/
|
||||
const customUpload: UploadMethod = (file, formData, options) => {
|
||||
const userStore = useUserStore()
|
||||
const uploadTask = uni.uploadFile({
|
||||
url: VITE_UPLOAD_BASEURL,
|
||||
header: {
|
||||
'X-Access-Token': userStore.userInfo.token,
|
||||
'X-Tenant-Id': userStore.userInfo.tenantId,
|
||||
...options.header,
|
||||
},
|
||||
name: options.name,
|
||||
fileName: options.name,
|
||||
fileType: options.fileType,
|
||||
formData,
|
||||
filePath: file.url,
|
||||
success(res: any) {
|
||||
if (res.statusCode === options.statusCode) {
|
||||
let data = res.data;
|
||||
if (data && isString(data)) {
|
||||
data = JSON.parse(data)
|
||||
}
|
||||
// 设置上传成功
|
||||
if (data && data.success) {
|
||||
const file = {
|
||||
id: new Date().getTime(),
|
||||
name: options.name,
|
||||
path: data.message,
|
||||
url: getFileAccessHttpUrl(data.message),
|
||||
}
|
||||
fileList.value.unshift(file)
|
||||
changeOnlineFormValue()
|
||||
}
|
||||
} else {
|
||||
// 设置上传失败
|
||||
options.onError({ ...res, errMsg: res.errMsg || '' }, file, formData)
|
||||
}
|
||||
},
|
||||
fail(err) {
|
||||
console.info('upload fail', err)
|
||||
// 设置上传失败
|
||||
options.onError(err, file, formData)
|
||||
},
|
||||
})
|
||||
// 设置当前文件加载的百分比
|
||||
uploadTask.onProgressUpdate((res) => {
|
||||
options.onProgress(res, file)
|
||||
})
|
||||
}
|
||||
|
||||
const changeOnlineFormValue = () => {
|
||||
console.log('changeOnlineFormValue fileList.value', fileList)
|
||||
const arr = fileList.value.map((item) => item['path'])
|
||||
const str = arr.join(',')
|
||||
emit('change', str)
|
||||
emit('update:value', str)
|
||||
}
|
||||
|
||||
const delFile = ({ file, fileList, resolve }) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除吗?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
console.log('当前删除文件', file)
|
||||
changeOnlineFormValue()
|
||||
toast.success('删除成功')
|
||||
resolve(true)
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err)
|
||||
resolve(false)
|
||||
},
|
||||
})
|
||||
}
|
||||
const loadFile = () => {
|
||||
if (!props.value || props.value.length === 0) {
|
||||
return
|
||||
}
|
||||
const pathArr = props.value.split(',')
|
||||
const fileArray = []
|
||||
pathArr.forEach((path) => {
|
||||
const seg = path.lastIndexOf('/')
|
||||
fileArray.push({
|
||||
name: path.substr(seg < 0 ? 0 : seg),
|
||||
path: path,
|
||||
url: getFileAccessHttpUrl(path),
|
||||
})
|
||||
})
|
||||
console.log('当前图片回显数据', fileArray)
|
||||
fileList.value = [...fileArray]
|
||||
}
|
||||
|
||||
// 监听 value 的变化
|
||||
watch(
|
||||
() => props.value,
|
||||
() => {
|
||||
loadFile()
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
// 组件挂载时加载文件
|
||||
onMounted(() => {
|
||||
loadFile()
|
||||
})
|
||||
</script>
|
||||
140
src/components/online/view/online-multi.vue
Normal file
140
src/components/online/view/online-multi.vue
Normal file
@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<wd-select-picker
|
||||
:label-width="labelWidth"
|
||||
:label="label"
|
||||
v-model="selected"
|
||||
filterable
|
||||
clearable
|
||||
:columns="options"
|
||||
:disabled="disabled"
|
||||
placeholder="请选择"
|
||||
@change="handleChange"
|
||||
></wd-select-picker>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from 'vue'
|
||||
import { isArray, isString } from 'lodash'
|
||||
import {http} from "@/utils/http";
|
||||
|
||||
// 定义 props
|
||||
const props = defineProps({
|
||||
dict: {
|
||||
type: [Array, String],
|
||||
default: () => [],
|
||||
required: true,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
labelWidth: {
|
||||
type: String,
|
||||
default: '80px',
|
||||
required: false,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
dictStr: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
value: {
|
||||
type: [Array, String],
|
||||
required: false,
|
||||
},
|
||||
modelValue: {
|
||||
type: [Array, String],
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
// 定义 emits
|
||||
const emit = defineEmits(['input', 'change', 'update:value'])
|
||||
|
||||
// 定义响应式数据
|
||||
const selected = ref([]);
|
||||
const options = ref([]);
|
||||
|
||||
// 初始化选项
|
||||
const initSelections = async () => {
|
||||
options.value = []
|
||||
if (props.type === 'sel_search' && props.dictStr) {
|
||||
let temp = props.dictStr
|
||||
if (temp.indexOf(' ') > 0) {
|
||||
temp = encodeURI(props.dictStr)
|
||||
}
|
||||
try {
|
||||
const res = await http.get('/sys/dict/getDictItems/' + temp)
|
||||
if (res.success) {
|
||||
options.value = res.result
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('请求数据出错:', error)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!props.dict || props.dict.length === 0) {
|
||||
return
|
||||
}
|
||||
if (isString(props.dict)) {
|
||||
try {
|
||||
const res = await http.get('/sys/dict/getDictItems/' + props.dict)
|
||||
if (res.success) {
|
||||
options.value = res.result
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('请求数据出错:', error)
|
||||
}
|
||||
} else {
|
||||
props.dict.forEach((item) => {
|
||||
options.value.push(item)
|
||||
})
|
||||
}
|
||||
}
|
||||
console.log("options.value ",options.value )
|
||||
}
|
||||
|
||||
// 选择器改变事件处理函数
|
||||
const handleChange = (e) => {
|
||||
let value = "";
|
||||
if (selected.value && isArray(selected.value)) {
|
||||
value = selected.value.join(',')
|
||||
}
|
||||
emit('update:value', value);
|
||||
emit('change', value);
|
||||
}
|
||||
|
||||
// 监听 dict 和 value 的变化
|
||||
watch(() => props.dict, () => {
|
||||
initSelections();
|
||||
});
|
||||
// 监听 value 的变化
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
selected.value = props?.modelValue? props.modelValue.split(','):[];
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
)
|
||||
// 组件挂载时初始化选项
|
||||
onMounted(() => {
|
||||
initSelections()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
100
src/components/online/view/online-pca.vue
Normal file
100
src/components/online/view/online-pca.vue
Normal file
@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<view>
|
||||
<wd-picker
|
||||
:columns="columns"
|
||||
:label-width="labelWidth"
|
||||
:label="label"
|
||||
:required="required"
|
||||
v-model="selected"
|
||||
:column-change="onChangeDistrict"
|
||||
@confirm="handleConfirm"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { areaData } from '@/components/online/area-picker-data'
|
||||
import {getAreaArrByCode} from '@/common/areaData/Area'
|
||||
import {isString} from "@/utils/is";
|
||||
// 接收 props
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
labelWidth: {
|
||||
type: String,
|
||||
default: '80px',
|
||||
required: false,
|
||||
},
|
||||
value: {
|
||||
type: [String,Array],
|
||||
required: false,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '请选择省市区',
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
// 定义 emits
|
||||
const emits = defineEmits(['input', 'change', 'update:value'])
|
||||
|
||||
// 定义响应式数据
|
||||
const selected = ref([])
|
||||
const district = { ...areaData }
|
||||
const columns = ref([
|
||||
district[0],
|
||||
district[district[0][0].value],
|
||||
district[district[district[0][0].value][0].value]
|
||||
])
|
||||
const onChangeDistrict = (pickerView, value, columnIndex, resolve) => {
|
||||
const item = value[columnIndex]
|
||||
if (columnIndex === 0) {
|
||||
pickerView.setColumnData(1, district[item.value])
|
||||
pickerView.setColumnData(2, district[district[item.value][0].value])
|
||||
} else if (columnIndex === 1) {
|
||||
pickerView.setColumnData(2, district[item.value])
|
||||
}
|
||||
resolve()
|
||||
}
|
||||
|
||||
const handleConfirm = ({value}) => {
|
||||
emits('update:value', value);
|
||||
}
|
||||
// 监听 value 变化
|
||||
watch(
|
||||
() => props.value,
|
||||
async (val) => {
|
||||
if(props.value && isString(props.value)){
|
||||
let arr = getAreaArrByCode(props.value);
|
||||
selected.value = arr;
|
||||
await initColumnData(arr);
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
/**
|
||||
* 初始化列数据
|
||||
* @param val
|
||||
*/
|
||||
function initColumnData(val){
|
||||
if(val && val.length){
|
||||
let first = district[0];
|
||||
let second = district[selected.value[0]];
|
||||
let third = district[selected.value[1]];
|
||||
columns.value = [first, second, third]
|
||||
}
|
||||
}
|
||||
</script>
|
||||
174
src/components/online/view/online-popup-link-record.vue
Normal file
174
src/components/online/view/online-popup-link-record.vue
Normal file
@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<view class="Popup">
|
||||
<view @click="handleClick">
|
||||
<wd-input
|
||||
:placeholder="`请选择${$attrs.label}`"
|
||||
type="text"
|
||||
readonly
|
||||
v-model="showText"
|
||||
clearable
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</view>
|
||||
<LinkRecordsModal
|
||||
v-if="reportModal.show"
|
||||
ref="lrmRef"
|
||||
:dictCode="dictCode"
|
||||
:dictTable="dictTable"
|
||||
:dictText="dictText"
|
||||
:multi="multi"
|
||||
:imageField="imageField"
|
||||
@close="handleClose"
|
||||
@change="handleChange"
|
||||
></LinkRecordsModal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, useAttrs } from 'vue'
|
||||
import { useToast } from 'wot-design-uni'
|
||||
import LinkRecordsModal from './link-records-modal.vue'
|
||||
import {http} from "@/utils/http";
|
||||
defineOptions({
|
||||
name: 'onlinePopupLinkRecord.vue',
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false,
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false,
|
||||
},
|
||||
formSchema: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['change', 'update:value','selected'])
|
||||
|
||||
const toast = useToast()
|
||||
const lrmRef = ref()
|
||||
const showText = ref('')
|
||||
const selectVal = ref([])
|
||||
const attrs: any = useAttrs()
|
||||
const reportModal = reactive({
|
||||
show: false,
|
||||
})
|
||||
//字典code
|
||||
const dictCode = computed(() => props.formSchema?.dictCode)
|
||||
//字典table
|
||||
const dictTable = computed(() => props.formSchema?.dictTable)
|
||||
//字典文本
|
||||
const dictText = computed(() => props.formSchema?.dictText)
|
||||
//是否多选
|
||||
const multi = computed(() => {
|
||||
if(props.formSchema?.fieldExtendJson){
|
||||
const extendJson = JSON.parse(props.formSchema.fieldExtendJson)
|
||||
return extendJson?.multiSelect
|
||||
}
|
||||
return false
|
||||
})
|
||||
//图片字段
|
||||
const imageField = computed(() => {
|
||||
if(props.formSchema?.fieldExtendJson){
|
||||
const extendJson = JSON.parse(props.formSchema.fieldExtendJson)
|
||||
return extendJson?.imageField
|
||||
}
|
||||
return ''
|
||||
})
|
||||
//首次加载
|
||||
const firstLoad = ref(true);
|
||||
/**
|
||||
* 监听value数值
|
||||
*/
|
||||
watch(
|
||||
() => props.value,
|
||||
(val) => {
|
||||
val && loadValue()
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
//加载数据
|
||||
function loadValue(){
|
||||
console.log('关联记录loadValue',firstLoad.value)
|
||||
if(!firstLoad.value){
|
||||
return
|
||||
}
|
||||
let linkTableSelectFields = dictCode.value + ',' + dictText.value;
|
||||
let superQueryParams = [{"field":"id","rule":"in","val": props.value}];
|
||||
let param = {
|
||||
linkTableSelectFields,
|
||||
superQueryMatchType:"and",
|
||||
superQueryParams: encodeURI(JSON.stringify(superQueryParams))
|
||||
};
|
||||
let titleField = props.formSchema?.dictText && props.formSchema?.dictText.split(",")[0];
|
||||
http.get(`/online/cgform/api/getData/${dictTable.value}`,param).then(res=>{
|
||||
if(res.success){
|
||||
let selectedList = res.result.records || [];
|
||||
let labels = [];
|
||||
let values = [];
|
||||
selectedList.forEach(item=>{
|
||||
if(item.id){
|
||||
values.push(item.id);
|
||||
labels.push(item[titleField]);
|
||||
}
|
||||
})
|
||||
showText.value = labels.join(',');
|
||||
selectVal.value = values;
|
||||
emit('selected', selectedList,props.name);
|
||||
}
|
||||
})
|
||||
firstLoad.value = false;
|
||||
}
|
||||
|
||||
//回显数值
|
||||
function callBack(rows) {
|
||||
//匹配popup设置的回调值
|
||||
let values = []
|
||||
let labels = []
|
||||
let titleField = props.formSchema?.dictText && props.formSchema?.dictText.split(",")[0];
|
||||
rows.forEach(item=>{
|
||||
if(item.id){
|
||||
values.push(item.id);
|
||||
labels.push(item[titleField]);
|
||||
}
|
||||
})
|
||||
showText.value = labels.join(',')
|
||||
selectVal.value = values
|
||||
emit('selected', rows,props.name)
|
||||
emit('change', values.join(','))
|
||||
emit('update:value', values.join(','))
|
||||
}
|
||||
//点击事件
|
||||
const handleClick = () => {
|
||||
if (!attrs.disabled) {
|
||||
reportModal.show = true
|
||||
//lrmRef.value.beforeOpen(selectVal.value)
|
||||
}
|
||||
}
|
||||
//关闭事件
|
||||
const handleClose = () => {
|
||||
reportModal.show = false
|
||||
}
|
||||
const handleChange = (data) => {
|
||||
console.log('选中的值:', data)
|
||||
callBack(data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
135
src/components/online/view/online-radio.vue
Normal file
135
src/components/online/view/online-radio.vue
Normal file
@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<wd-select-picker
|
||||
:label-width="labelWidth"
|
||||
:label="label"
|
||||
v-model="selected"
|
||||
type="radio"
|
||||
filterable
|
||||
clearable
|
||||
:columns="options"
|
||||
:disabled="disabled"
|
||||
placeholder="请选择"
|
||||
@change="handleChange"
|
||||
></wd-select-picker>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from 'vue'
|
||||
import { isArray, isString } from 'lodash'
|
||||
import {http} from "@/utils/http"; // 假设使用 lodash 来判断类型
|
||||
|
||||
// 定义 props
|
||||
const props = defineProps({
|
||||
dict: {
|
||||
type: [Array, String],
|
||||
default: () => [],
|
||||
required: true,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
labelWidth: {
|
||||
type: String,
|
||||
default: '80px',
|
||||
required: false,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
dictStr: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
value: {
|
||||
type: [Array, String],
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
// 定义 emits
|
||||
const emit = defineEmits(['input', 'change', 'update:value'])
|
||||
|
||||
// 定义响应式数据
|
||||
const selected = ref([]);
|
||||
const options = ref([]);
|
||||
|
||||
// 初始化选项
|
||||
const initSelections = async () => {
|
||||
options.value = []
|
||||
if (props.type === 'sel_search' && props.dictStr) {
|
||||
let temp = props.dictStr
|
||||
if (temp.indexOf(' ') > 0) {
|
||||
temp = encodeURI(props.dictStr)
|
||||
}
|
||||
try {
|
||||
const res = await http.get('/sys/dict/getDictItems/' + temp)
|
||||
if (res.success) {
|
||||
options.value = res.result
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('请求数据出错:', error)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!props.dict || props.dict.length === 0) {
|
||||
return
|
||||
}
|
||||
if (isString(props.dict)) {
|
||||
try {
|
||||
const res = await http.get('/sys/dict/getDictItems/' + props.dict)
|
||||
if (res.success) {
|
||||
options.value = res.result;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('请求数据出错:', error)
|
||||
}
|
||||
} else {
|
||||
props.dict.forEach((item) => {
|
||||
options.value.push(item)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 选择器改变事件处理函数
|
||||
const handleChange = (e) => {
|
||||
emit('update:value', selected.value);
|
||||
emit('change', selected.value);
|
||||
}
|
||||
|
||||
// 监听 dict 和 value 的变化
|
||||
watch(() => props.dict, () => {
|
||||
initSelections();
|
||||
});
|
||||
// 监听 value 的变化
|
||||
watch(
|
||||
() => props.value,
|
||||
(val) => {
|
||||
console.log("字典单选》》》》》 ",val)
|
||||
selected.value = val;
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
// 组件挂载时初始化选项
|
||||
onMounted(() => {
|
||||
initSelections()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
132
src/components/online/view/online-select.vue
Normal file
132
src/components/online/view/online-select.vue
Normal file
@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<wd-picker
|
||||
:label-width="labelWidth"
|
||||
:label="label"
|
||||
filterable
|
||||
v-model="selected"
|
||||
:columns="options"
|
||||
:disabled="disabled"
|
||||
placeholder="请选择"
|
||||
@change="handleChange"
|
||||
></wd-picker>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from 'vue'
|
||||
import { isArray, isString } from 'lodash'
|
||||
import {http} from "@/utils/http"; // 假设使用 lodash 来判断类型
|
||||
|
||||
// 定义 props
|
||||
const props = defineProps({
|
||||
dict: {
|
||||
type: [Array, String],
|
||||
default: () => [],
|
||||
required: true,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
labelWidth: {
|
||||
type: String,
|
||||
default: '80px',
|
||||
required: false,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
dictStr: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
// 定义 emits
|
||||
const emit = defineEmits(['input', 'change', 'update:value'])
|
||||
|
||||
// 定义响应式数据
|
||||
const selected = ref('请选择')
|
||||
const options = ref([])
|
||||
|
||||
// 初始化选项
|
||||
const initSelections = async () => {
|
||||
options.value = []
|
||||
if (props.type === 'sel_search' && props.dictStr) {
|
||||
let temp = props.dictStr
|
||||
if (temp.indexOf(' ') > 0) {
|
||||
temp = encodeURI(props.dictStr)
|
||||
}
|
||||
try {
|
||||
const res = await http.get('/sys/dict/getDictItems/' + temp)
|
||||
if (res.success) {
|
||||
options.value = res.result;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('请求数据出错:', error)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!props.dict || props.dict.length === 0) {
|
||||
return
|
||||
}
|
||||
if (isString(props.dict)) {
|
||||
try {
|
||||
const res = await http.get('/sys/dict/getDictItems/' + props.dict)
|
||||
if (res.success) {
|
||||
options.value = res.result;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('请求数据出错:', error)
|
||||
}
|
||||
} else {
|
||||
props.dict.forEach((item) => {
|
||||
options.value.push(item)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 选择器改变事件处理函数
|
||||
const handleChange = (e) => {
|
||||
emit('update:value', selected.value);
|
||||
emit('change', selected.value);
|
||||
}
|
||||
|
||||
// 监听 dict 和 value 的变化
|
||||
watch(() => props.dict, () => {
|
||||
initSelections();
|
||||
});
|
||||
// 监听 value 的变化
|
||||
watch(
|
||||
() => props.value,
|
||||
(val) => {
|
||||
selected.value = !val? [] : props.value;
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
// 组件挂载时初始化选项
|
||||
onMounted(() => {
|
||||
initSelections()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
58
src/components/online/view/online-time.vue
Normal file
58
src/components/online/view/online-time.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<wd-datetime-picker :disabled="disabled" type="time" :labelWidth="labelWidth" v-model="currentTime" :label="label" @confirm="handleConfirm" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 定义 props
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
labelWidth: {
|
||||
type: String,
|
||||
default: '80px',
|
||||
required: false,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
// 定义 emits
|
||||
const emit = defineEmits(['input', 'change', 'update:value'])
|
||||
// 定义响应式数据;
|
||||
const currentTime = ref('')
|
||||
|
||||
// 监听 value 的变化
|
||||
watch(
|
||||
() => props.value,
|
||||
(val) => {
|
||||
currentTime.value = val?val:'';
|
||||
}
|
||||
)
|
||||
// 选择器改变事件处理函数
|
||||
const handleConfirm = (e) => {
|
||||
emit('update:value', currentTime.value+':00')
|
||||
emit('change', currentTime.value+':00')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
101
src/components/registerGlobComp.ts
Normal file
101
src/components/registerGlobComp.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import type { App } from 'vue';
|
||||
import StatusTip from '@/pages-work/components/statusTip.vue';
|
||||
import JBar from '@/pages-work/components/echarts/JBar/index.vue';
|
||||
import JBackgroundBar from '@/pages-work/components/echarts/JBackgroundBar/index.vue';
|
||||
import JDynamicBar from '@/pages-work/components/echarts/JDynamicBar/index.vue';
|
||||
import JStackBar from '@/pages-work/components/echarts/JStackBar/index.vue';
|
||||
import JMultipleBar from '@/pages-work/components/echarts/JMultipleBar/index.vue';
|
||||
import JNegativeBar from '@/pages-work/components/echarts/JNegativeBar/index.vue';
|
||||
import JMixLineBar from '@/pages-work/components/echarts/JMixLineBar/index.vue';
|
||||
import JProgress from '@/pages-work/components/echarts/JProgress/index.vue';
|
||||
import JLine from '@/pages-work/components/echarts/JLine/index.vue';
|
||||
import JMultipleLine from '@/pages-work/components/echarts/JMultipleLine/index.vue';
|
||||
import JSmoothLine from '@/pages-work/components/echarts/JSmoothLine/index.vue';
|
||||
import JStepLine from '@/pages-work/components/echarts/JStepLine/index.vue';
|
||||
import JPie from '@/pages-work/components/echarts/JPie/index.vue';
|
||||
import JRing from '@/pages-work/components/echarts/JRing/index.vue';
|
||||
import JFunnel from '@/pages-work/components/echarts/JFunnel/index.vue';
|
||||
import JPyramidFunnel from '@/pages-work/components/echarts/JPyramidFunnel/index.vue';
|
||||
import JRadar from '@/pages-work/components/echarts/JRadar/index.vue';
|
||||
import JCircleRadar from '@/pages-work/components/echarts/JCircleRadar/index.vue';
|
||||
import JGauge from '@/pages-work/components/echarts/JGauge/index.vue';
|
||||
import JColorGauge from '@/pages-work/components/echarts/JColorGauge/index.vue';
|
||||
import JScatter from '@/pages-work/components/echarts/JScatter/index.vue';
|
||||
import JBubble from '@/pages-work/components/echarts/JBubble/index.vue';
|
||||
import DoubleLineBar from '@/pages-work/components/echarts/DoubleLineBar/index.vue';
|
||||
import JRose from '@/pages-work/components/echarts/JRose/index.vue';
|
||||
import JHorizontalBar from '@/pages-work/components/echarts/JHorizontalBar/index.vue';
|
||||
import JArea from '@/pages-work/components/echarts/JArea/index.vue';
|
||||
import JPictorial from '@/pages-work/components/echarts/JPictorial/index.vue';
|
||||
import JPictorialBar from '@/pages-work/components/echarts/JPictorialBar/index.vue';
|
||||
import JAreaMap from '@/pages-work/components/echarts/map/JAreaMap/index.vue';
|
||||
import JBubbleMap from '@/pages-work/components/echarts/map/JBubbleMap/index.vue';
|
||||
import JBarMap from '@/pages-work/components/echarts/map/JBarMap/index.vue';
|
||||
import JHeatMap from '@/pages-work/components/echarts/map/JHeatMap/index.vue';
|
||||
import JFlyLineMap from '@/pages-work/components/echarts/map/JFlyLineMap/index.vue';
|
||||
|
||||
//非echart组件
|
||||
import JCarousel from '@/pages-work/components/drag/carousel/index.vue';
|
||||
import JIframe from '@/pages-work/components/drag/iframe/index.vue';
|
||||
import JDragEditor from '@/pages-work/components/drag/editor/index.vue';
|
||||
import JImg from '@/pages-work/components/drag/img/index.vue';
|
||||
import JNumber from '@/pages-work/components/drag/number/index.vue';
|
||||
import JText from '@/pages-work/components/drag/text/index.vue';
|
||||
import JCalendar from '@/pages-work/components/drag/calendar/index.vue';
|
||||
import JCurrentTime from '@/pages-work/components/drag/currentTime/time.vue';
|
||||
import JList from '@/pages-work/components/drag/list/index.vue';
|
||||
import JRadioButton from '@/pages-work/components/drag/radiobutton/index.vue';
|
||||
import JCommonTable from '@/pages-work/components/drag/table/index.vue';
|
||||
import JQuickNav from '@/pages-work/components/drag/JQuickNav/index.vue';
|
||||
|
||||
// 全局注册组件
|
||||
export function registerGlobComp(app: App) {
|
||||
app.component('statusTip', StatusTip)
|
||||
app.component('JBar', JBar)
|
||||
app.component('JMultipleBar', JMultipleBar)
|
||||
app.component('JNegativeBar', JNegativeBar)
|
||||
app.component('JLine', JLine)
|
||||
app.component('JMultipleLine', JMultipleLine)
|
||||
app.component('JPie', JPie)
|
||||
app.component('JRing', JRing)
|
||||
app.component('JFunnel', JFunnel)
|
||||
app.component('JPyramidFunnel', JPyramidFunnel)
|
||||
app.component('JRadar', JRadar)
|
||||
app.component('JCircleRadar', JCircleRadar)
|
||||
app.component('JGauge', JGauge)
|
||||
app.component('JColorGauge', JColorGauge)
|
||||
app.component('JScatter', JScatter)
|
||||
app.component('JBubble', JBubble)
|
||||
app.component('DoubleLineBar', DoubleLineBar)
|
||||
app.component('JRose', JRose)
|
||||
app.component('JHorizontalBar', JHorizontalBar)
|
||||
app.component('JArea', JArea)
|
||||
app.component('JBackgroundBar', JBackgroundBar)
|
||||
app.component('JDynamicBar', JDynamicBar)
|
||||
app.component('JMixLineBar', JMixLineBar)
|
||||
app.component('JStackBar', JStackBar)
|
||||
app.component('JStepLine', JStepLine)
|
||||
app.component('JSmoothLine', JSmoothLine)
|
||||
app.component('JProgress', JProgress)
|
||||
app.component('JPictorial', JPictorial)
|
||||
app.component('JPictorialBar', JPictorialBar)
|
||||
app.component('JAreaMap', JAreaMap)
|
||||
app.component('JBubbleMap', JBubbleMap)
|
||||
app.component('JBarMap', JBarMap)
|
||||
app.component('JHeatMap', JHeatMap)
|
||||
app.component('JFlyLineMap', JFlyLineMap)
|
||||
|
||||
//非echart组件
|
||||
app.component('JCarousel', JCarousel)
|
||||
app.component('JIframe', JIframe)
|
||||
app.component('JDragEditor', JDragEditor)
|
||||
app.component('JImg', JImg)
|
||||
app.component('JNumber', JNumber)
|
||||
app.component('JText', JText)
|
||||
app.component('JCalendar', JCalendar)
|
||||
app.component('JCurrentTime', JCurrentTime)
|
||||
app.component('JList', JList)
|
||||
app.component('JRadioButton', JRadioButton)
|
||||
app.component('JCommonTable', JCommonTable)
|
||||
app.component('JQuickNav', JQuickNav)
|
||||
}
|
||||
33
src/env.d.ts
vendored
Normal file
33
src/env.d.ts
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/// <reference types="vite/client" />
|
||||
/// <reference types="vite-svg-loader" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
|
||||
interface ImportMetaEnv {
|
||||
/** 网站标题,应用名称 */
|
||||
readonly VITE_APP_TITLE: string
|
||||
/** 服务端口号 */
|
||||
readonly VITE_SERVER_PORT: string
|
||||
/** 后台接口地址 */
|
||||
readonly VITE_SERVER_BASEURL: string
|
||||
/** H5是否需要代理 */
|
||||
readonly VITE_APP_PROXY: 'true' | 'false'
|
||||
/** H5是否需要代理,需要的话有个前缀 */
|
||||
readonly VITE_APP_PROXY_PREFIX: string // 一般是/api
|
||||
/** 上传图片地址 */
|
||||
readonly VITE_UPLOAD_BASEURL: string
|
||||
/** 是否清除console */
|
||||
readonly VITE_DELETE_CONSOLE: string
|
||||
/** 是否开启mock */
|
||||
readonly VITE_USE_MOCK: 'true' | 'false'
|
||||
// 更多环境变量...
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv
|
||||
}
|
||||
44
src/hooks/useRequest.ts
Normal file
44
src/hooks/useRequest.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { UnwrapRef } from 'vue'
|
||||
|
||||
type IUseRequestOptions<T> = {
|
||||
/** 是否立即执行 */
|
||||
immediate?: boolean
|
||||
/** 初始化数据 */
|
||||
initialData?: T
|
||||
}
|
||||
|
||||
/**
|
||||
* useRequest是一个定制化的请求钩子,用于处理异步请求和响应。
|
||||
* @param func 一个执行异步请求的函数,返回一个包含响应数据的Promise。
|
||||
* @param options 包含请求选项的对象 {immediate, initialData}。
|
||||
* @param options.immediate 是否立即执行请求,默认为false。
|
||||
* @param options.initialData 初始化数据,默认为undefined。
|
||||
* @returns 返回一个对象{loading, error, data, run},包含请求的加载状态、错误信息、响应数据和手动触发请求的函数。
|
||||
*/
|
||||
export default function useRequest<T>(
|
||||
func: () => Promise<IResData<T>>,
|
||||
options: IUseRequestOptions<T> = { immediate: false },
|
||||
) {
|
||||
const loading = ref(false)
|
||||
const error = ref(false)
|
||||
const data = ref<T>(options.initialData)
|
||||
const run = async () => {
|
||||
loading.value = true
|
||||
return func()
|
||||
.then((res) => {
|
||||
data.value = res.data as UnwrapRef<T>
|
||||
error.value = false
|
||||
return data.value
|
||||
})
|
||||
.catch((err) => {
|
||||
error.value = err
|
||||
throw err
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
options.immediate && run()
|
||||
return { loading, error, data, run }
|
||||
}
|
||||
86
src/hooks/useUpload.ts
Normal file
86
src/hooks/useUpload.ts
Normal file
@ -0,0 +1,86 @@
|
||||
// TODO: 别忘加更改环境变量的 VITE_UPLOAD_BASEURL 地址。
|
||||
import { getEnvBaseUploadUrl } from '@/utils'
|
||||
import { useUserStore } from '@/store/user'
|
||||
|
||||
const VITE_UPLOAD_BASEURL = `${getEnvBaseUploadUrl()}`
|
||||
|
||||
/**
|
||||
* useUpload 是一个定制化的请求钩子,用于处理上传图片。
|
||||
* @param formData 额外传递给后台的数据,如{name: '张三'}。
|
||||
* @returns 返回一个对象{loading, error, data, run},包含请求的加载状态、错误信息、响应数据和手动触发请求的函数。
|
||||
*/
|
||||
export default function useUpload<T = string>(
|
||||
formData: Record<string, any> = {},
|
||||
{ url, sizeType = ['original', 'compressed'], sourceType = ['album', 'camera'] },
|
||||
) {
|
||||
const loading = ref(false)
|
||||
const error = ref(false)
|
||||
const data = ref<T>()
|
||||
const run = () => {
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
|
||||
// 微信小程序在2023年10月17日之后,使用本API需要配置隐私协议
|
||||
uni.chooseMedia({
|
||||
count: 1,
|
||||
mediaType: ['image'],
|
||||
sourceType,
|
||||
sizeType, //可以指定是原图还是压缩图,默认二者都有
|
||||
success: (res) => {
|
||||
loading.value = true
|
||||
const tempFilePath = res.tempFiles[0].tempFilePath
|
||||
const fileName = res.type
|
||||
formData.fileName = fileName;
|
||||
uploadFile<T>({ url, tempFilePath, formData, data, error, loading, fileName })
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('uni.chooseMedia err->', err)
|
||||
error.value = true
|
||||
},
|
||||
})
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sourceType, //从相册选择
|
||||
sizeType, //可以指定是原图还是压缩图,默认二者都有
|
||||
success: (res) => {
|
||||
loading.value = true
|
||||
const tempFilePath = res.tempFilePaths[0]
|
||||
const fileName = res.tempFiles[0].name
|
||||
formData.fileName = fileName;
|
||||
uploadFile<T>({ url, tempFilePath, formData, data, error, loading })
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('uni.chooseImage err->', err)
|
||||
error.value = true
|
||||
},
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
return { loading, error, data, run }
|
||||
}
|
||||
|
||||
function uploadFile<T>({ url, tempFilePath, formData, data, error, loading }) {
|
||||
const userStore = useUserStore()
|
||||
uni.uploadFile({
|
||||
url: url ?? VITE_UPLOAD_BASEURL,
|
||||
filePath: tempFilePath,
|
||||
name: 'file',
|
||||
formData,
|
||||
header: {
|
||||
'X-Access-Token': userStore.userInfo.token,
|
||||
'X-Tenant-Id': userStore.userInfo.tenantId,
|
||||
},
|
||||
success: (uploadFileRes) => {
|
||||
data.value = JSON.parse(uploadFileRes.data)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('uni.uploadFile err->', err)
|
||||
error.value = true
|
||||
},
|
||||
complete: () => {
|
||||
loading.value = false
|
||||
},
|
||||
})
|
||||
}
|
||||
3
src/interceptors/index.ts
Normal file
3
src/interceptors/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { routeInterceptor } from './route'
|
||||
export { requestInterceptor } from './request'
|
||||
export { prototypeInterceptor } from './prototype'
|
||||
13
src/interceptors/prototype.ts
Normal file
13
src/interceptors/prototype.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export const prototypeInterceptor = {
|
||||
install() {
|
||||
// 解决低版本手机不识别 array.at() 导致运行报错的问题
|
||||
if (typeof Array.prototype.at !== 'function') {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Array.prototype.at = function (index: number) {
|
||||
if (index < 0) return this[this.length + index]
|
||||
if (index >= this.length) return undefined
|
||||
return this[index]
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
68
src/interceptors/request.ts
Normal file
68
src/interceptors/request.ts
Normal file
@ -0,0 +1,68 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import qs from 'qs'
|
||||
import { useUserStore } from '@/store'
|
||||
import { platform } from '@/utils/platform'
|
||||
import { getEnvBaseUrl } from '@/utils'
|
||||
|
||||
export type CustomRequestOptions = UniApp.RequestOptions & {
|
||||
query?: Record<string, any>
|
||||
/** 出错时是否隐藏错误提示 */
|
||||
hideErrorToast?: boolean
|
||||
} & IUniUploadFileOptions // 添加uni.uploadFile参数类型
|
||||
|
||||
// 请求基准地址
|
||||
const baseUrl = getEnvBaseUrl()
|
||||
|
||||
// 拦截器配置
|
||||
const httpInterceptor = {
|
||||
// 拦截前触发
|
||||
invoke(options: CustomRequestOptions) {
|
||||
// 接口请求支持通过 query 参数配置 queryString
|
||||
if (options.query) {
|
||||
const queryStr = qs.stringify(options.query)
|
||||
if (options.url.includes('?')) {
|
||||
options.url += `&${queryStr}`
|
||||
} else {
|
||||
options.url += `?${queryStr}`
|
||||
}
|
||||
}
|
||||
// 非 http 开头需拼接地址
|
||||
if (!options.url.startsWith('http')) {
|
||||
// #ifdef H5
|
||||
// console.log(__VITE_APP_PROXY__)
|
||||
if (JSON.parse(__VITE_APP_PROXY__)) {
|
||||
// 啥都不需要做
|
||||
} else {
|
||||
options.url = baseUrl + options.url
|
||||
}
|
||||
// #endif
|
||||
// 非H5正常拼接
|
||||
// #ifndef H5
|
||||
options.url = baseUrl + options.url
|
||||
// #endif
|
||||
// TIPS: 如果需要对接多个后端服务,也可以在这里处理,拼接成所需要的地址
|
||||
}
|
||||
// 1. 请求超时
|
||||
options.timeout = 10000 // 10s
|
||||
// 2. (可选)添加小程序端请求头标识
|
||||
options.header = {
|
||||
platform, // 可选,与 uniapp 定义的平台一致,告诉后台来源
|
||||
...options.header,
|
||||
}
|
||||
// 3. 添加 token 请求头标识
|
||||
const userStore = useUserStore()
|
||||
const { token } = userStore.userInfo as unknown as IUserInfo
|
||||
if (token) {
|
||||
options.header.Authorization = `${token}`
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export const requestInterceptor = {
|
||||
install() {
|
||||
// 拦截 request 请求
|
||||
uni.addInterceptor('request', httpInterceptor)
|
||||
// 拦截 uploadFile 文件上传
|
||||
uni.addInterceptor('uploadFile', httpInterceptor)
|
||||
},
|
||||
}
|
||||
53
src/interceptors/route.ts
Normal file
53
src/interceptors/route.ts
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 路由拦截,通常也是登录拦截
|
||||
* 可以设置路由白名单,或者黑名单,看业务需要选哪一个
|
||||
* 我这里应为大部分都可以随便进入,所以使用黑名单
|
||||
*/
|
||||
import { useUserStore } from '@/store'
|
||||
import { needLoginPages as _needLoginPages, getNeedLoginPages } from '@/utils'
|
||||
|
||||
// TODO Check
|
||||
const loginRoute = '/pages/login/index'
|
||||
|
||||
const isLogined = () => {
|
||||
const userStore = useUserStore()
|
||||
return userStore.isLogined
|
||||
}
|
||||
|
||||
const isDev = import.meta.env.DEV
|
||||
|
||||
// 黑名单登录拦截器 - (适用于大部分页面不需要登录,少部分页面需要登录)
|
||||
const navigateToInterceptor = {
|
||||
// 注意,这里的url是 '/' 开头的,如 '/pages/index/index',跟 'pages.json' 里面的 path 不同
|
||||
invoke({ url }: { url: string }) {
|
||||
// console.log(url) // /pages/route-interceptor/index?name=feige&age=30
|
||||
const path = url.split('?')[0]
|
||||
let needLoginPages: string[] = []
|
||||
// 为了防止开发时出现BUG,这里每次都获取一下。生产环境可以移到函数外,性能更好
|
||||
if (isDev) {
|
||||
needLoginPages = getNeedLoginPages()
|
||||
} else {
|
||||
needLoginPages = _needLoginPages
|
||||
}
|
||||
const isNeedLogin = needLoginPages.includes(path)
|
||||
if (!isNeedLogin) {
|
||||
return true
|
||||
}
|
||||
const hasLogin = isLogined()
|
||||
if (hasLogin) {
|
||||
return true
|
||||
}
|
||||
const redirectRoute = `${loginRoute}?redirect=${encodeURIComponent(url)}`
|
||||
uni.navigateTo({ url: redirectRoute })
|
||||
return false
|
||||
},
|
||||
}
|
||||
|
||||
export const routeInterceptor = {
|
||||
install() {
|
||||
uni.addInterceptor('navigateTo', navigateToInterceptor)
|
||||
uni.addInterceptor('reLaunch', navigateToInterceptor)
|
||||
uni.addInterceptor('redirectTo', navigateToInterceptor)
|
||||
uni.addInterceptor('switchTab', navigateToInterceptor)
|
||||
},
|
||||
}
|
||||
18
src/layouts/default.vue
Normal file
18
src/layouts/default.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<wd-config-provider :themeVars="themeVars">
|
||||
<slot />
|
||||
<!-- <wd-toast />
|
||||
<wd-message-box />
|
||||
<wd-notify /> -->
|
||||
</wd-config-provider>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { ConfigProviderThemeVars } from 'wot-design-uni'
|
||||
const themeVars: ConfigProviderThemeVars = {
|
||||
// colorTheme: 'red',
|
||||
// buttonPrimaryBgColor: '#07c160',
|
||||
// buttonPrimaryColor: '#07c160',
|
||||
}
|
||||
</script>
|
||||
17
src/layouts/demo.vue
Normal file
17
src/layouts/demo.vue
Normal file
@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<wd-config-provider :themeVars="themeVars">
|
||||
<slot />
|
||||
<wd-toast />
|
||||
<wd-message-box />
|
||||
</wd-config-provider>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { ConfigProviderThemeVars } from 'wot-design-uni'
|
||||
|
||||
const themeVars: ConfigProviderThemeVars = {
|
||||
// colorTheme: 'red',
|
||||
// buttonPrimaryBgColor: '#07c160',
|
||||
// buttonPrimaryColor: '#07c160',
|
||||
}
|
||||
</script>
|
||||
30
src/main.ts
Normal file
30
src/main.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import '@/style/index.scss'
|
||||
import '@/style/custom/main.css'
|
||||
import '@/style/custom/icon.css'
|
||||
import '@/style/custom/animation.css'
|
||||
import { VueQueryPlugin } from '@tanstack/vue-query'
|
||||
import 'virtual:uno.css'
|
||||
import { createSSRApp } from 'vue'
|
||||
|
||||
import App from './App.vue'
|
||||
import { prototypeInterceptor, requestInterceptor, routeInterceptor } from './interceptors'
|
||||
import { registerGlobComp } from '@/components/registerGlobComp';
|
||||
import store from './store'
|
||||
import router from './router'
|
||||
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
app.use(store)
|
||||
app.use(router)
|
||||
app.use(routeInterceptor)
|
||||
app.use(requestInterceptor)
|
||||
app.use(prototypeInterceptor)
|
||||
app.use(VueQueryPlugin)
|
||||
//#ifndef MP-WEIXIN
|
||||
// 注册全局组件
|
||||
registerGlobComp(app);
|
||||
// #endif
|
||||
return {
|
||||
app,
|
||||
}
|
||||
}
|
||||
139
src/manifest.json
Normal file
139
src/manifest.json
Normal file
@ -0,0 +1,139 @@
|
||||
{
|
||||
"name": "JeecgBoot-uniapp",
|
||||
"appid": "__UNI__9F097F0",
|
||||
"description": "",
|
||||
"versionName": "1.0.0",
|
||||
"versionCode": "100",
|
||||
"transformPx": false,
|
||||
"app-plus": {
|
||||
"usingComponents": true,
|
||||
"nvueStyleCompiler": "uni-app",
|
||||
"compilerVersion": 3,
|
||||
"splashscreen": {
|
||||
"alwaysShowBeforeRender": true,
|
||||
"waiting": true,
|
||||
"autoclose": true,
|
||||
"delay": 0
|
||||
},
|
||||
"modules": {
|
||||
"Maps": {},
|
||||
"Messaging": {},
|
||||
"Contacts": {},
|
||||
"Camera": {}
|
||||
},
|
||||
"distribute": {
|
||||
"android": {
|
||||
"permissions": [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
],
|
||||
"minSdkVersion": 30,
|
||||
"targetSdkVersion": 30,
|
||||
"abiFilters": [
|
||||
"armeabi-v7a",
|
||||
"arm64-v8a"
|
||||
]
|
||||
},
|
||||
"ios": {},
|
||||
"sdkConfigs": {
|
||||
"maps": {
|
||||
"amap": {
|
||||
"name": "amap_15931993294Bqxlq8EgG",
|
||||
"appkey_ios": "c913e46ffdf548ebc56ac1cf4d883e7e",
|
||||
"appkey_android": "c913e46ffdf548ebc56ac1cf4d883e7e"
|
||||
}
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
"android": {
|
||||
"hdpi": "src/static/app/icons/72x72.png",
|
||||
"xhdpi": "src/static/app/icons/96x96.png",
|
||||
"xxhdpi": "src/static/app/icons/144x144.png",
|
||||
"xxxhdpi": "src/static/app/icons/192x192.png"
|
||||
},
|
||||
"ios": {
|
||||
"appstore": "src/static/app/icons/1024x1024.png",
|
||||
"ipad": {
|
||||
"app": "src/static/app/icons/76x76.png",
|
||||
"app@2x": "src/static/app/icons/152x152.png",
|
||||
"notification": "src/static/app/icons/20x20.png",
|
||||
"notification@2x": "src/static/app/icons/40x40.png",
|
||||
"proapp@2x": "src/static/app/icons/167x167.png",
|
||||
"settings": "src/static/app/icons/29x29.png",
|
||||
"settings@2x": "src/static/app/icons/58x58.png",
|
||||
"spotlight": "src/static/app/icons/40x40.png",
|
||||
"spotlight@2x": "src/static/app/icons/80x80.png"
|
||||
},
|
||||
"iphone": {
|
||||
"app@2x": "src/static/app/icons/120x120.png",
|
||||
"app@3x": "src/static/app/icons/180x180.png",
|
||||
"notification@2x": "src/static/app/icons/40x40.png",
|
||||
"notification@3x": "src/static/app/icons/60x60.png",
|
||||
"settings@2x": "src/static/app/icons/58x58.png",
|
||||
"settings@3x": "src/static/app/icons/87x87.png",
|
||||
"spotlight@2x": "src/static/app/icons/80x80.png",
|
||||
"spotlight@3x": "src/static/app/icons/120x120.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"compatible": {
|
||||
"ignoreVersion": true
|
||||
}
|
||||
},
|
||||
"quickapp": {},
|
||||
"mp-weixin": {
|
||||
"appid": "wx8e287639924edb51",
|
||||
"setting": {
|
||||
"urlCheck": false,
|
||||
"minified": true
|
||||
},
|
||||
"usingComponents": true
|
||||
},
|
||||
"mp-alipay": {
|
||||
"usingComponents": true,
|
||||
"styleIsolation": "shared"
|
||||
},
|
||||
"mp-baidu": {
|
||||
"usingComponents": true
|
||||
},
|
||||
"mp-toutiao": {
|
||||
"usingComponents": true
|
||||
},
|
||||
"uniStatistics": {
|
||||
"enable": false
|
||||
},
|
||||
"vueVersion": "3",
|
||||
"h5": {
|
||||
"router": {
|
||||
"base": "/"
|
||||
},
|
||||
"sdkConfigs": {
|
||||
"maps": {
|
||||
"amap": {
|
||||
"key": "21f194a0d33197f874f7bbdd198419be",
|
||||
"securityJsCode": "a46b425f31a4de445b2966d998fba851",
|
||||
"serviceHost": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"app-harmony": {
|
||||
"distribute": {
|
||||
"bundleName": "uniapp.demo.test"
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/package.json
Normal file
19
src/package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"id": "da-tree",
|
||||
"name": "da-tree 树组件(支持单选、多选、无限级、主题色,Vue3版) ",
|
||||
"displayName": "da-tree 树组件(支持单选、多选、无限级、主题色,Vue3版) ",
|
||||
"version": "1.4.2",
|
||||
"description": "一个基于 Vue3 的tree(树)组件,支持主题换色,可能是最适合你的tree(树)组件",
|
||||
"keywords": [
|
||||
"tree",
|
||||
"树",
|
||||
"树组件",
|
||||
"da系列"
|
||||
],
|
||||
"dcloudext": {
|
||||
"category": [
|
||||
"前端组件",
|
||||
"通用组件"
|
||||
]
|
||||
}
|
||||
}
|
||||
20
src/pages-home/home/home.vue
Normal file
20
src/pages-home/home/home.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<route lang="json5" type="page">
|
||||
{
|
||||
layout: 'default',
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
},
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<view class=""></view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
//
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
//
|
||||
</style>
|
||||
396
src/pages-message/chat/chat.vue
Normal file
396
src/pages-message/chat/chat.vue
Normal file
@ -0,0 +1,396 @@
|
||||
<route lang="json5" type="page">
|
||||
{
|
||||
layout: 'default',
|
||||
style: {
|
||||
navigationBarTitleText: '聊天',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<PageLayout :navTitle="navTitle" backRouteName="message" routeMethod="pushTab">
|
||||
<view class="wrap">
|
||||
<!-- prettier-ignore -->
|
||||
<z-paging ref="paging" v-model="dataList" :fixed="false" use-chat-record-mode use-virtual-list cell-height-mode="dynamic" safe-area-inset-bottom bottom-bg-color="#e5e5e5" @query="queryList" @keyboardHeightChange="keyboardHeightChange" @hidedKeyboard="hidedKeyboard">
|
||||
<template #cell="{item,index}">
|
||||
<view style="transform: scaleY(-1)">
|
||||
<chat-item :item="item" :playMsgid="playMsgid" @playVoice="handlePlayVoice"></chat-item>
|
||||
</view>
|
||||
</template>
|
||||
<template #bottom>
|
||||
<chat-input-bar ref="inputBar" @send="doSend" @image="handleImage" />
|
||||
</template>
|
||||
</z-paging>
|
||||
</view>
|
||||
</PageLayout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
//
|
||||
import { onLaunch, onShow, onHide, onLoad, onReady } from '@dcloudio/uni-app'
|
||||
import { nextTick, onMounted, ref } from 'vue'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { http } from '@/utils/http'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { useRouter } from '@/plugin/uni-mini-router'
|
||||
import { cache, getFileAccessHttpUrl, hasRoute, formatDate } from '@/common/uitls'
|
||||
import { TENANT_LIST } from '@/common/constants'
|
||||
import socket from '@/common/socket'
|
||||
import { textReplaceEmoji, getEmojiImageUrl } from './emojis'
|
||||
import chatInputBar from './components/chat-input-bar.vue'
|
||||
import chatItem from './components/chat-item.vue'
|
||||
import { getEnvBaseUrl } from '@/utils/index'
|
||||
import { useParamsStore } from '@/store/page-params'
|
||||
|
||||
defineOptions({
|
||||
name: 'chat',
|
||||
options: {
|
||||
// apply-shared:当前页面样式会影响到子组件样式.(小程序)
|
||||
// shared:当前页面样式影响到子组件,子组件样式也会影响到当前页面.(小程序)
|
||||
styleIsolation: 'apply-shared',
|
||||
},
|
||||
})
|
||||
|
||||
const api = {
|
||||
chatlog_old: '/eoa/im/api/queryChatLogList',
|
||||
chatlog: '/eoa/im/newApi/records',
|
||||
sendMsg: '/eoa/im/newApi/sendMessage',
|
||||
sendFile: '/eoa/im/newApi/sendFile',
|
||||
creatFriendSession: '/eoa/im/newApi/creatFriendSession',
|
||||
uploadUrl: `${getEnvBaseUrl()}/eoa/im/newApi/sendImage`,
|
||||
}
|
||||
|
||||
const toast = useToast()
|
||||
const userStore = useUserStore()
|
||||
const paging = ref(null)
|
||||
|
||||
// 聊天对方用户信息
|
||||
const chatObj = ref(null)
|
||||
const navTitle = ref('')
|
||||
// 对方userid
|
||||
const chatto = ref()
|
||||
const myuid = ref(userStore.userInfo.userid)
|
||||
const msgList = ref([])
|
||||
// const pageNo = ref(1)
|
||||
// const pageSize = ref(10)
|
||||
const loadingShow = ref(false)
|
||||
const hasRecord = ref(false)
|
||||
const dataList = ref([])
|
||||
const inputBar = ref(null)
|
||||
const AUDIO = uni.createInnerAudioContext()
|
||||
const playMsgid = ref('')
|
||||
let stopWatch: any = null
|
||||
const paramsStore = useParamsStore()
|
||||
|
||||
// 页面初始化
|
||||
const init = () => {
|
||||
const localData = paramsStore.getPageParams('chat')
|
||||
const params = localData.data
|
||||
if (!params) {
|
||||
return
|
||||
}
|
||||
chatObj.value = { ...params }
|
||||
navTitle.value = params.fromUserName
|
||||
chatto.value = chatObj.value.msgTo
|
||||
creatFriendSession(chatObj.value.msgTo)
|
||||
onSocketOpen()
|
||||
onSocketReceive()
|
||||
getMsgList()
|
||||
}
|
||||
// 创建会话数据
|
||||
const creatFriendSession = (userId) => {
|
||||
http.post(api.creatFriendSession, {
|
||||
type: 'friend',
|
||||
userId,
|
||||
})
|
||||
}
|
||||
const onSocketOpen = () => {
|
||||
console.log('启动webSocket')
|
||||
socket.init('eoaNewChatSocket')
|
||||
}
|
||||
const onSocketReceive = () => {
|
||||
var _this = this
|
||||
socket.acceptMessage = function (res) {
|
||||
console.log('页面收到的消息=====》', res)
|
||||
if (res.event == 'event_talk_revoke') {
|
||||
// 撤回了消息
|
||||
removeMsg(res)
|
||||
} else {
|
||||
if (res.type == 'friend') {
|
||||
//聊天消息
|
||||
screenMsg(res)
|
||||
unreadClear()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const removeMsg = (data) => {
|
||||
let arr = msgList.value.filter((item) => item.id != data.id)
|
||||
msgList.value = arr
|
||||
}
|
||||
const screenMsg = (msg) => {
|
||||
//消息处理
|
||||
if (msg.msgFrom == chatto.value && msg.msgTo == myuid.value) {
|
||||
console.log('用户消息')
|
||||
let time = formatDate(msg.sendTime, 'yyyy-MM-dd hh:mm:ss')
|
||||
let id = time.replace(/\:/g, '').replace(/\-/g, '').replace(' ', '')
|
||||
let content = msg.msgData
|
||||
if (msg.msgType == 'text') {
|
||||
content = replaceEmoji(content)
|
||||
}
|
||||
if (msg.msgType == 'voice') {
|
||||
content = JSON.parse(content)
|
||||
}
|
||||
msgList.value.push({
|
||||
fromUserName: msg.fromUserName,
|
||||
msgTo: msg.msgTo,
|
||||
msgFrom: msg.msgFrom,
|
||||
msgData: content,
|
||||
fromAvatar: msg.fromAvatar,
|
||||
sendTime: time,
|
||||
msgType: msg.msgType,
|
||||
sendTimeId: id,
|
||||
fileName: msg.fileName,
|
||||
id: msg.id,
|
||||
})
|
||||
//非自己的消息震动
|
||||
if (msg.msgFrom != myuid.value) {
|
||||
console.log('振动')
|
||||
uni.vibrateLong()
|
||||
}
|
||||
// this.$nextTick(function () {
|
||||
// // 滚动到底
|
||||
// this.scrollToView = 'msg' + id
|
||||
// })
|
||||
}
|
||||
}
|
||||
//替换表情符号为图片
|
||||
const replaceEmoji = (str) => {
|
||||
let temp = textReplaceEmoji(str)
|
||||
return '<div style="display:inline-block">' + temp + '</div>'
|
||||
}
|
||||
|
||||
const queryList = (pageNo, pageSize) => {
|
||||
//数据库查询消息列表
|
||||
let params = {
|
||||
type: 'friend',
|
||||
pageNo: pageNo,
|
||||
pageSize: pageSize,
|
||||
msgTo: chatto.value,
|
||||
id: myuid.value,
|
||||
sort: 'DESC',
|
||||
}
|
||||
console.log('params', params)
|
||||
http
|
||||
.get(api.chatlog, params)
|
||||
.then((res: any) => {
|
||||
if (res.success && res.result?.records) {
|
||||
const records = analysis(res.result.records)
|
||||
paging.value.complete(records)
|
||||
} else {
|
||||
paging.value.complete(false)
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
paging.value.complete(false)
|
||||
})
|
||||
}
|
||||
const analysis = (data) => {
|
||||
let arr = data
|
||||
if (arr.length > 0) {
|
||||
let list = arr.map((item) => {
|
||||
let id = item.sendTime.replace(/\:/g, '').replace(/\-/g, '').replace(' ', '')
|
||||
item.sendTimeId = id
|
||||
let content = item.msgData
|
||||
if (item.msgType == 'text') {
|
||||
content = replaceEmoji(content)
|
||||
}
|
||||
if (item.msgType == 'voice') {
|
||||
content = JSON.parse(content)
|
||||
}
|
||||
item.msgData = content
|
||||
return item
|
||||
})
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].msgType == 'revoke') {
|
||||
continue
|
||||
}
|
||||
if (list[i].referenceMsgId) {
|
||||
list[i] = handleReplyMsg(list[i], list)
|
||||
}
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// 加载初始页面消息
|
||||
const getMsgList = () => {}
|
||||
const handleReplyMsg = (item, list) => {
|
||||
let tempId = item.referenceMsgId
|
||||
item.reply = true
|
||||
let replyContent = ''
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].id == tempId) {
|
||||
replyContent = '"' + list[i].fromUserName + ':' + list[i].msgData + '"'
|
||||
break
|
||||
}
|
||||
}
|
||||
item.replyContent = replyContent
|
||||
return item
|
||||
}
|
||||
const unreadClear = () => {
|
||||
http
|
||||
.post('/eoa/im/newApi/unreadClear', {
|
||||
type: chatObj.value.type,
|
||||
msgTo: chatObj.value.msgTo,
|
||||
msgFrom: chatObj.value.msgFrom,
|
||||
})
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
// _this.eventChannel.emit('toPrePageData', { data: 'data from chat page' })
|
||||
}
|
||||
})
|
||||
}
|
||||
// 播放语音
|
||||
const handlePlayVoice = (item) => {
|
||||
if (item.id == playMsgid.value) {
|
||||
AUDIO.stop()
|
||||
playMsgid.value = ''
|
||||
} else {
|
||||
playMsgid.value = item.id
|
||||
AUDIO.src = item.msgData.url
|
||||
nextTick(function () {
|
||||
AUDIO.play()
|
||||
})
|
||||
}
|
||||
}
|
||||
// 播放语音结束
|
||||
AUDIO.onEnded((res) => {
|
||||
playMsgid.value = ''
|
||||
})
|
||||
|
||||
// 监听键盘高度改变,请不要直接通过uni.onKeyboardHeightChange监听,否则可能导致z-paging内置的键盘高度改变监听失效(如果不需要切换表情面板则不用写)
|
||||
const keyboardHeightChange = (res) => {
|
||||
inputBar.value.updateKeyboardHeightChange(res)
|
||||
}
|
||||
// 用户尝试隐藏键盘,此时如果表情面板在展示中,应当通知chatInputBar隐藏表情面板(如果不需要切换表情面板则不用写)
|
||||
const hidedKeyboard = () => {
|
||||
inputBar.value.hidedKeyboard()
|
||||
}
|
||||
const doSend = (textMsg) => {
|
||||
// paging.value.addChatRecordData([
|
||||
// {
|
||||
// time: '',
|
||||
// icon: '/static/daxiong.jpg',
|
||||
// name: '大雄',
|
||||
// content: msg,
|
||||
// isMe: true,
|
||||
// },
|
||||
// ])
|
||||
|
||||
let content = replaceEmoji(textMsg)
|
||||
//let msg = {'text':content}
|
||||
//content = (content||'').replace(/&(?!#?[a-zA-Z0-9]+;)/g, '&')
|
||||
console.log('content', content)
|
||||
let msg = textMsg
|
||||
let time = formatDate(new Date().getTime(), 'yyyy-MM-dd hh:mm:ss')
|
||||
let id = time.replace(/\:/g, '').replace(/\-/g, '').replace(' ', '')
|
||||
//发送
|
||||
sendMsg(msg, 'text')
|
||||
paging.value.addChatRecordData(
|
||||
analysis([
|
||||
{
|
||||
fromUserName: userStore.userInfo.realname,
|
||||
msgTo: chatto.value,
|
||||
msgFrom: myuid.value,
|
||||
msgData: content,
|
||||
fromAvatar: userStore.userInfo.avatar,
|
||||
sendTime: time,
|
||||
sendTimeId: id,
|
||||
msgType: 'text',
|
||||
},
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
const sendMsg = (content, type) => {
|
||||
//实际应用中,此处应该提交长连接,模板仅做本地处理。
|
||||
var nowDate = new Date()
|
||||
// 发送消息
|
||||
var obj = {
|
||||
mine: {
|
||||
avatar: userStore.userInfo.avatar,
|
||||
content: content,
|
||||
id: myuid.value,
|
||||
mine: true,
|
||||
username: userStore.userInfo.username,
|
||||
},
|
||||
to: {
|
||||
avatar: chatObj.value.avatar,
|
||||
id: chatObj.value.msgTo,
|
||||
type: 'friend',
|
||||
username: chatObj.value.username,
|
||||
},
|
||||
}
|
||||
|
||||
let sendData = {
|
||||
type: 'chatMessage',
|
||||
data: obj,
|
||||
}
|
||||
console.log('sendData======>', sendData)
|
||||
let params = {
|
||||
type: 'friend',
|
||||
msgTo: chatObj.value.msgTo,
|
||||
text: content,
|
||||
msgType: 'text',
|
||||
}
|
||||
http.post(api.sendMsg, params).then((res: any) => {
|
||||
console.log('消息发送结果:', res)
|
||||
if (!res.success) {
|
||||
toast.error(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleImage = (type) => {
|
||||
let time = formatDate(new Date().getTime(), 'yyyy-MM-dd hh:mm:ss')
|
||||
let id = time.replace(/\:/g, '').replace(/\-/g, '').replace(' ', '')
|
||||
let formData = {
|
||||
type: 'friend',
|
||||
msgTo: chatto.value,
|
||||
fileId: id,
|
||||
msgType: 'images',
|
||||
fileName: '',
|
||||
}
|
||||
const { loading, data, error, run } = useUpload(
|
||||
{ ...formData, name: 'image' },
|
||||
{ url: api.uploadUrl, sourceType: [type] },
|
||||
)
|
||||
if (stopWatch) stopWatch()
|
||||
run()
|
||||
stopWatch = watch(
|
||||
() => [loading.value, error.value, data.value],
|
||||
([loading, err, data], oldValue) => {
|
||||
if (loading == false) {
|
||||
if (err) {
|
||||
toast.warning('修改失败')
|
||||
uni.hideLoading()
|
||||
} else {
|
||||
if (data) {
|
||||
console.log('data::', data)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
init()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
//
|
||||
.wrap {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
229
src/pages-message/chat/components/chat-input-bar.vue
Normal file
229
src/pages-message/chat/components/chat-input-bar.vue
Normal file
@ -0,0 +1,229 @@
|
||||
<!-- z-paging聊天输入框 -->
|
||||
|
||||
<template>
|
||||
<view class="chat-input-bar-container">
|
||||
<view class="chat-input-bar">
|
||||
<view class="add-container" @click="tooglePanl('more')">
|
||||
<view class="icon add"></view>
|
||||
</view>
|
||||
<view class="chat-input-container">
|
||||
<!-- :adjust-position="false"必须设置,防止键盘弹窗自动上顶,交由z-paging内部处理 -->
|
||||
<input
|
||||
:focus="focus"
|
||||
class="chat-input"
|
||||
v-model="msg"
|
||||
:adjust-position="false"
|
||||
confirm-type="send"
|
||||
type="text"
|
||||
placeholder="请输入内容"
|
||||
@confirm="sendClick"
|
||||
/>
|
||||
</view>
|
||||
<!-- 表情图标(如果不需要切换表情面板则不用写) -->
|
||||
<view class="emoji-container">
|
||||
<image class="emoji-img" :src="getEmoji" @click="tooglePanl('emoji')"></image>
|
||||
</view>
|
||||
<view class="chat-input-send" @click="sendClick">
|
||||
<text class="chat-input-send-text">发送</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 表情面板,这里使用height控制隐藏显示是为了有高度变化的动画效果(如果不需要切换表情面板则不用写) -->
|
||||
<view
|
||||
class="emoji-panel-container"
|
||||
:style="[{ height: ['emoji', 'more'].includes(chatBarType) ? '320rpx' : '0px' }]"
|
||||
>
|
||||
<scroll-view scroll-y style="height: 100%; flex: 1">
|
||||
<template v-if="['emoji'].includes(chatBarType)">
|
||||
<!-- 表情 -->
|
||||
<view class="emoji-panel">
|
||||
<swiper class="emoji-swiper zdybq" :indicator-dots="true" :duration="150">
|
||||
<swiper-item class="swiperItem" v-for="(page, pid) in emojiArray" :key="pid">
|
||||
<view class="item" v-for="(em, eid) in page" :key="eid" @tap="emojiClick(em)">
|
||||
<image mode="scaleToFill" :src="em.url" style="width: 28px; height: 28px"></image>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
</template>
|
||||
<template v-if="['more'].includes(chatBarType)">
|
||||
<!-- 相册、照相 -->
|
||||
<view class="more-panel">
|
||||
<view class="box" @tap="getImage('album')"><view class="icon tupian2"></view></view>
|
||||
<view class="box" @tap="getImage('camera')"><view class="icon paizhao"></view></view>
|
||||
</view>
|
||||
</template>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { textReplaceEmoji, getEmojiImageUrl } from '../emojis'
|
||||
import { computed } from 'vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'chat-input-bar',
|
||||
options: {
|
||||
// apply-shared:当前页面样式会影响到子组件样式.(小程序)
|
||||
// shared:当前页面样式影响到子组件,子组件样式也会影响到当前页面.(小程序)
|
||||
styleIsolation: 'apply-shared',
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['emojiTypeChange', 'send', 'image'])
|
||||
let emojiArray = getEmojiImageUrl()
|
||||
const msg = ref('')
|
||||
// 当前input focus
|
||||
const focus = ref(false)
|
||||
// emoji、more
|
||||
const chatBarType = ref('')
|
||||
|
||||
const getEmoji = computed(() => {
|
||||
let img
|
||||
if (['', 'more'].includes(chatBarType.value)) {
|
||||
img = 'emoji'
|
||||
} else if (['emoji'].includes(chatBarType.value)) {
|
||||
img = 'keyboard'
|
||||
}
|
||||
return `/static/chat/${img}.png`
|
||||
})
|
||||
|
||||
const updateKeyboardHeightChange = (res) => {
|
||||
if (res.height > 0) {
|
||||
chatBarType.value = ''
|
||||
}
|
||||
}
|
||||
const hidedKeyboard = () => {
|
||||
if (['emoji', 'more'].includes(chatBarType.value)) {
|
||||
chatBarType.value = ''
|
||||
}
|
||||
}
|
||||
// 点击了切换表情面板/键盘(如果不需要切换表情面板则不用写)
|
||||
const tooglePanl = (val) => {
|
||||
if (chatBarType.value == val) {
|
||||
// 点击了键盘,展示键盘
|
||||
focus.value = true
|
||||
chatBarType.value = ''
|
||||
} else {
|
||||
// 点击了切换表情面板
|
||||
focus.value = false
|
||||
// 隐藏键盘
|
||||
uni.hideKeyboard()
|
||||
chatBarType.value = val
|
||||
}
|
||||
}
|
||||
// 点击了某个表情,将其插入输入内容中(如果不需要切换表情面板则不用写)
|
||||
const emojiClick = (em) => {
|
||||
msg.value += em.alt
|
||||
}
|
||||
|
||||
// 点击了发送按钮
|
||||
const sendClick = () => {
|
||||
if (!msg.value.length) return
|
||||
emit('send', msg.value)
|
||||
msg.value = ''
|
||||
}
|
||||
// 点击了发送按钮
|
||||
const getImage = (type) => {
|
||||
emit('image', type)
|
||||
}
|
||||
defineExpose({
|
||||
updateKeyboardHeightChange,
|
||||
hidedKeyboard,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.chat-input-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
border-top: solid 1px #f5f5f5;
|
||||
background-color: #f8f8f8;
|
||||
|
||||
padding: 10rpx 20rpx;
|
||||
}
|
||||
.add-container {
|
||||
margin-right: 8px;
|
||||
.icon {
|
||||
font-size: 26px;
|
||||
}
|
||||
}
|
||||
.chat-input-container {
|
||||
flex: 1;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
padding: 15rpx;
|
||||
background-color: white;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
.chat-input {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.emoji-container {
|
||||
width: 54rpx;
|
||||
height: 54rpx;
|
||||
margin: 10rpx 0rpx 10rpx 20rpx;
|
||||
}
|
||||
.emoji-img {
|
||||
width: 54rpx;
|
||||
height: 54rpx;
|
||||
}
|
||||
.chat-input-send {
|
||||
background-color: #007aff;
|
||||
margin: 10rpx 10rpx 10rpx 20rpx;
|
||||
border-radius: 10rpx;
|
||||
width: 110rpx;
|
||||
height: 60rpx;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.chat-input-send-text {
|
||||
color: white;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
.emoji-panel-container {
|
||||
border-top: 1px solid #e8e8e8;
|
||||
background-color: #f8f8f8;
|
||||
overflow: hidden;
|
||||
transition-property: height;
|
||||
transition-duration: 0.15s;
|
||||
}
|
||||
.emoji-panel {
|
||||
height: 100%;
|
||||
padding: 0 8vw;
|
||||
.swiperItem {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
.item {
|
||||
width: 12vw;
|
||||
height: 12vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
.more-panel {
|
||||
display: flex;
|
||||
padding-top: 3vw;
|
||||
.box {
|
||||
width: 18vw;
|
||||
height: 18vw;
|
||||
border-radius: 10px;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0 3vw 2vw 3vw;
|
||||
.icon {
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
174
src/pages-message/chat/components/chat-item.vue
Normal file
174
src/pages-message/chat/components/chat-item.vue
Normal file
@ -0,0 +1,174 @@
|
||||
<!-- z-paging聊天item -->
|
||||
|
||||
<template>
|
||||
<view class="chat-item">
|
||||
<text class="chat-time" v-if="item.sendTime && item.sendTime.length">
|
||||
{{ item.sendTime }}
|
||||
</text>
|
||||
<view :class="{ 'chat-container': true, 'chat-location-me': isMe(item) }">
|
||||
<view class="chat-icon-container">
|
||||
<image class="chat-icon" :src="item.fromAvatar" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="chat-content-container">
|
||||
<text :class="{ 'chat-user-name': true, 'chat-location-me': isMe(item) }">
|
||||
{{ item.fromUserName }}
|
||||
</text>
|
||||
<view
|
||||
:class="{
|
||||
'chat-text-container-super': true,
|
||||
'flex-end': isMe(item),
|
||||
'flex-start': !isMe(item),
|
||||
}"
|
||||
>
|
||||
<!---文字-->
|
||||
<template v-if="['text'].includes(item.msgType)">
|
||||
<view :class="{ 'chat-text-container': true, 'chat-text-container-me': isMe(item) }">
|
||||
<text :class="{ 'chat-text': true, 'chat-text-me': isMe(item) }">
|
||||
<rich-text :nodes="item.msgData"></rich-text>
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
<!--图片-->
|
||||
<template v-else-if="['image'].includes(item.msgType)">
|
||||
<wd-img
|
||||
width="200"
|
||||
height="200"
|
||||
:enable-preview="true"
|
||||
:radius="10"
|
||||
:src="getFileAccessHttpUrl(item.msgData)"
|
||||
></wd-img>
|
||||
</template>
|
||||
<!--语音-->
|
||||
<template v-else-if="['voice'].includes(item.msgType)">
|
||||
<view
|
||||
:class="{
|
||||
'chat-voice-container': true,
|
||||
'chat-voice-container-me': isMe(item),
|
||||
play: playMsgid == item.id,
|
||||
}"
|
||||
@click="playVoice(item)"
|
||||
>
|
||||
<view class="length mr-2">{{ item.msgData.length }}</view>
|
||||
<view class="icon my-voice"></view>
|
||||
</view>
|
||||
</template>
|
||||
<!--文件-->
|
||||
<template v-else-if="['file'].includes(item.msgType)"></template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { cache, getFileAccessHttpUrl } from '@/common/uitls'
|
||||
|
||||
defineOptions({ name: 'chat-item' })
|
||||
|
||||
const userStore = useUserStore()
|
||||
const emit = defineEmits(['playVoice'])
|
||||
const props = defineProps({
|
||||
playMsgid: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
item: {
|
||||
type: Object,
|
||||
default: function () {
|
||||
return {
|
||||
sendTime: '',
|
||||
fromAvatar: '',
|
||||
fromUserName: '',
|
||||
msgData: '',
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
const isMe = (item) => {
|
||||
return item.msgFrom == userStore.userInfo.userid
|
||||
}
|
||||
const playVoice = (item) => {
|
||||
emit('playVoice', item)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.chat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20upx;
|
||||
}
|
||||
.chat-time {
|
||||
padding: 4upx 0upx;
|
||||
text-align: center;
|
||||
font-size: 22upx;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
.chat-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.chat-location-me {
|
||||
flex-direction: row-reverse;
|
||||
text-align: right;
|
||||
}
|
||||
.chat-icon-container {
|
||||
margin-top: 12upx;
|
||||
}
|
||||
.chat-icon {
|
||||
width: 80upx;
|
||||
height: 80upx;
|
||||
border-radius: 8px;
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
.chat-content-container {
|
||||
margin: 0upx 15upx;
|
||||
}
|
||||
.chat-user-name {
|
||||
font-size: 26upx;
|
||||
color: #888888;
|
||||
}
|
||||
.chat-text-container,
|
||||
.chat-voice-container {
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
border-radius: 8upx;
|
||||
padding: 7px 10px;
|
||||
margin-top: 10upx;
|
||||
/* #ifndef APP-NVUE */
|
||||
max-width: 500upx;
|
||||
/* #endif */
|
||||
}
|
||||
.chat-text-container-me,
|
||||
.chat-voice-container {
|
||||
background-color: #55aaff;
|
||||
}
|
||||
.chat-voice-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
}
|
||||
.chat-text-container-super {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
&.flex-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
&.flex-start {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
.chat-text {
|
||||
font-size: 28upx;
|
||||
/* #ifndef APP-NVUE */
|
||||
word-break: break-all;
|
||||
/* #endif */
|
||||
/* #ifdef APP-NVUE */
|
||||
max-width: 500upx;
|
||||
/* #endif */
|
||||
}
|
||||
.chat-text-me {
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
439
src/pages-message/chat/emojis.ts
Normal file
439
src/pages-message/chat/emojis.ts
Normal file
@ -0,0 +1,439 @@
|
||||
/**
|
||||
* 动态表情
|
||||
*/
|
||||
export const emojis = {
|
||||
'[微笑]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/1-1.png'>",
|
||||
'[撇嘴]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/1-2.png'>",
|
||||
'[色]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/1-3.png'>",
|
||||
'[发呆]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/1-4.png'>",
|
||||
'[得意]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/1-5.png'>",
|
||||
'[流泪]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/1-6.png'>",
|
||||
'[害羞]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/1-7.png'>",
|
||||
'[闭嘴]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/1-8.png'>",
|
||||
'[睡]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/1-9.png'>",
|
||||
'[大哭]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/1-10.png'>",
|
||||
'[尴尬]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/1-11.png'>",
|
||||
'[发怒]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/1-12.png'>",
|
||||
|
||||
'[调皮]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/2-1.png'>",
|
||||
'[呲牙]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/2-2.png'>",
|
||||
'[惊讶]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/2-3.png'>",
|
||||
'[难过]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/2-4.png'>",
|
||||
'[酷]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/2-5.png'>",
|
||||
'[冷汗]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/2-6.png'>",
|
||||
'[抓狂]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/2-7.png'>",
|
||||
'[吐]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/2-8.png'>",
|
||||
'[偷笑]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/2-9.png'>",
|
||||
'[可爱]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/2-10.png'>",
|
||||
'[白眼]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/2-11.png'>",
|
||||
'[傲慢]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/2-12.png'>",
|
||||
|
||||
'[饥饿]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/3-1.png'>",
|
||||
'[困]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/3-2.png'>",
|
||||
'[惊恐]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/3-3.png'>",
|
||||
'[流汗]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/3-4.png'>",
|
||||
'[憨笑]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/3-5.png'>",
|
||||
'[悠闲]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/3-6.png'>",
|
||||
'[奋斗]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/3-7.png'>",
|
||||
'[咒骂]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/3-8.png'>",
|
||||
'[疑问]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/3-9.png'>",
|
||||
'[嘘]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/3-10.png'>",
|
||||
'[晕]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/3-11.png'>",
|
||||
'[折磨]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/3-12.png'>",
|
||||
|
||||
'[衰]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/4-1.png'>",
|
||||
'[骷髅]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/4-2.png'>",
|
||||
'[敲打]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/4-3.png'>",
|
||||
'[再见]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/4-4.png'>",
|
||||
'[擦汗]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/4-5.png'>",
|
||||
'[抠鼻]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/4-6.png'>",
|
||||
'[鼓掌]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/4-7.png'>",
|
||||
'[糗大了]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/4-8.png'>",
|
||||
'[坏笑]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/4-9.png'>",
|
||||
'[左哼哼]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/4-10.png'>",
|
||||
'[右哼哼]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/4-11.png'>",
|
||||
'[哈欠]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/4-12.png'>",
|
||||
|
||||
'[鄙视]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/5-1.png'>",
|
||||
'[委屈]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/5-2.png'>",
|
||||
'[快哭了]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/5-3.png'>",
|
||||
'[阴险]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/5-4.png'>",
|
||||
'[右亲亲]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/5-5.png'>",
|
||||
'[左亲亲]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/5-6.png'>",
|
||||
'[吓]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/5-7.png'>",
|
||||
'[可怜]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/5-8.png'>",
|
||||
'[眨眼睛]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/5-9.png'>",
|
||||
'[笑哭]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/5-10.png'>",
|
||||
'[doge]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/5-11.png'>",
|
||||
'[泪奔]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/5-12.png'>",
|
||||
|
||||
'[无奈]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/6-1.png'>",
|
||||
'[托腮]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/6-2.png'>",
|
||||
'[卖萌]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/6-3.png'>",
|
||||
'[斜眼笑]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/6-4.png'>",
|
||||
'[喷血]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/6-5.png'>",
|
||||
'[惊喜]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/6-6.png'>",
|
||||
'[骚扰]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/6-7.png'>",
|
||||
'[小纠结]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/6-8.png'>",
|
||||
'[我最美]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/6-9.png'>",
|
||||
'[加油必胜]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/6-10.png'>",
|
||||
'[加油抱抱]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/6-11.png'>",
|
||||
'[口罩护体]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/6-12.png'>",
|
||||
|
||||
'[搬砖中]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/7-1.png'>",
|
||||
'[忙到飞起]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/7-2.png'>",
|
||||
'[脑阔疼]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/7-3.png'>",
|
||||
'[沧桑]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/7-4.png'>",
|
||||
'[捂脸]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/7-5.png'>",
|
||||
'[辣眼睛]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/7-6.png'>",
|
||||
'[哦呦]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/7-7.png'>",
|
||||
'[头秃]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/7-8.png'>",
|
||||
'[问号脸]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/7-9.png'>",
|
||||
'[暗中观察]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/7-10.png'>",
|
||||
'[emm]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/7-11.png'>",
|
||||
'[吃瓜]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/7-12.png'>",
|
||||
|
||||
'[呵呵哒]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/8-1.png'>",
|
||||
'[汪汪]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/8-2.png'>",
|
||||
'[牛转钱坤]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/8-3.png'>",
|
||||
'[牛气冲天]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/8-4.png'>",
|
||||
'[无眼笑]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/8-5.png'>",
|
||||
'[敬礼]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/8-6.png'>",
|
||||
'[狂笑]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/8-7.png'>",
|
||||
'[面无表情]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/8-8.png'>",
|
||||
'[摸鱼]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/8-9.png'>",
|
||||
'[摸锦鲤]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/8-10.png'>",
|
||||
'[魔鬼笑]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/8-11.png'>",
|
||||
'[哦]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/8-12.png'>",
|
||||
|
||||
'[请]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/9-1.png'>",
|
||||
'[睁眼]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/9-2.png'>",
|
||||
'[期待]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/9-3.png'>",
|
||||
'[拜谢]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/9-4.png'>",
|
||||
'[元宝]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/9-5.png'>",
|
||||
'[牛啊]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/9-6.png'>",
|
||||
'[胖三斤]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/9-7.png'>",
|
||||
'[好闪]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/9-8.png'>",
|
||||
'[打call]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/9-9.png'>",
|
||||
'[变形]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/9-10.png'>",
|
||||
'[仔细分析]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/9-11.png'>",
|
||||
'[加油]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/9-12.png'>",
|
||||
|
||||
'[菜汪]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/10-1.png'>",
|
||||
'[崇拜]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/10-2.png'>",
|
||||
'[比心]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/10-3.png'>",
|
||||
'[庆祝]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/10-4.png'>",
|
||||
'[吃糖]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/10-5.png'>",
|
||||
'[花朵脸]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/10-6.png'>",
|
||||
'[我想开了]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/10-7.png'>",
|
||||
'[舔屏]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/10-8.png'>",
|
||||
'[热化了]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/10-9.png'>",
|
||||
'[我酸了]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/10-10.png'>",
|
||||
'[拿到红包]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/10-11.png'>",
|
||||
'[豹富]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/10-12.png'>",
|
||||
|
||||
'[握手]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/11-1.png'>",
|
||||
'[胜利]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/11-2.png'>",
|
||||
'[抱拳]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/11-3.png'>",
|
||||
'[勾引]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/11-4.png'>",
|
||||
'[拳头]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/11-5.png'>",
|
||||
'[差劲]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/11-6.png'>",
|
||||
'[爱你]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/11-7.png'>",
|
||||
'[NO]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/11-8.png'>",
|
||||
'[OK]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/11-9.png'>",
|
||||
'[拜托]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/11-10.gif'>",
|
||||
'[惬意]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/11-11.gif'>",
|
||||
'[孤寂]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/11-12.gif'>",
|
||||
|
||||
'[菜刀]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/12-1.png'>",
|
||||
'[西瓜]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/12-2.png'>",
|
||||
'[啤酒]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/12-3.png'>",
|
||||
'[篮球]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/12-4.png'>",
|
||||
'[茶]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/12-5.png'>",
|
||||
'[咖啡]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/12-6.png'>",
|
||||
'[饭]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/12-7.png'>",
|
||||
'[玫瑰]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/12-8.png'>",
|
||||
'[凋谢]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/12-9.png'>",
|
||||
'[爱心]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/12-10.png'>",
|
||||
'[心碎]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/12-11.png'>",
|
||||
'[示爱]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/12-12.png'>",
|
||||
|
||||
'[炸弹]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/13-1.png'>",
|
||||
'[刀]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/13-2.png'>",
|
||||
'[足球]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/13-3.png'>",
|
||||
'[瓢虫]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/13-4.png'>",
|
||||
'[便便]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/13-5.png'>",
|
||||
'[月亮]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/13-6.png'>",
|
||||
'[太阳]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/13-7.png'>",
|
||||
'[礼物]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/13-8.png'>",
|
||||
'[抱抱]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/13-9.png'>",
|
||||
'[猪头]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/13-10.png'>",
|
||||
'[乒乓球]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/13-11.png'>",
|
||||
'[蛋糕]':
|
||||
"<img src='https://jeecgos.oss-cn-beijing.aliyuncs.com/files/appimg/chat_emoticon/13-12.png'>",
|
||||
}
|
||||
|
||||
/**
|
||||
* 符号表情
|
||||
*/
|
||||
const symbol = [
|
||||
'😠',
|
||||
'😩',
|
||||
'😲',
|
||||
'😞',
|
||||
'😵',
|
||||
'😰',
|
||||
'😒',
|
||||
'😍',
|
||||
'😤',
|
||||
'😜',
|
||||
'😝',
|
||||
'😋',
|
||||
'😘',
|
||||
'😚',
|
||||
'😷',
|
||||
'😳',
|
||||
'😃',
|
||||
'😅',
|
||||
'😆',
|
||||
'😁',
|
||||
'😂',
|
||||
'😊',
|
||||
'☺',
|
||||
'😄',
|
||||
'😢',
|
||||
'😭',
|
||||
'😨',
|
||||
'😣',
|
||||
'😡',
|
||||
'😌',
|
||||
'😖',
|
||||
'😔',
|
||||
'😱',
|
||||
'😪',
|
||||
'😏',
|
||||
'😓',
|
||||
'😥',
|
||||
'😫',
|
||||
'😉',
|
||||
'✊',
|
||||
'✋',
|
||||
'✌',
|
||||
'👊',
|
||||
'👍',
|
||||
'☝',
|
||||
'👆',
|
||||
'👇',
|
||||
'👈',
|
||||
'👉',
|
||||
'👋',
|
||||
'👏',
|
||||
'👌',
|
||||
'👎',
|
||||
]
|
||||
|
||||
const emojisKeys = Object.keys(emojis)
|
||||
|
||||
export const emojiList = {
|
||||
symbol,
|
||||
emojis,
|
||||
}
|
||||
|
||||
const regEmoji = emojisKeys
|
||||
.map((value) => '|\\' + value)
|
||||
.join('')
|
||||
.replace('|', '')
|
||||
|
||||
/**
|
||||
* 替换表情文字
|
||||
*
|
||||
* @param {String} content 需要替换的字符串
|
||||
*/
|
||||
export function textReplaceEmoji(content) {
|
||||
return content.replace(new RegExp(`(${regEmoji})`, 'gi'), ($0, $1) => {
|
||||
return emojis[$1]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 将表情存到数组中 用于选择
|
||||
* 21个表情是一个数组
|
||||
*/
|
||||
export function getEmojiImageUrl() {
|
||||
let arr = []
|
||||
let keys = Object.keys(emojis)
|
||||
let index = 0
|
||||
let reg = /^.*src=\'(.*)\'>$/
|
||||
while (index < keys.length) {
|
||||
let sub = []
|
||||
for (let i = 0; i < 21; i++) {
|
||||
let temp = index + i
|
||||
if (!keys[temp]) {
|
||||
continue
|
||||
}
|
||||
let url = emojis[keys[temp]].match(reg)[1]
|
||||
sub.push({
|
||||
url,
|
||||
alt: keys[temp],
|
||||
})
|
||||
}
|
||||
index += 21
|
||||
arr.push(sub)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
439
src/pages-message/common/vue-py.ts
Normal file
439
src/pages-message/common/vue-py.ts
Normal file
@ -0,0 +1,439 @@
|
||||
const pinyin = {
|
||||
'a': '\u554a\u963f\u9515',
|
||||
'ai': '\u57c3\u6328\u54ce\u5509\u54c0\u7691\u764c\u853c\u77ee\u827e\u788d\u7231\u9698\u8bf6\u6371\u55f3\u55cc\u5ad2\u7477\u66a7\u7839\u953f\u972d',
|
||||
'an': '\u978d\u6c28\u5b89\u4ffa\u6309\u6697\u5cb8\u80fa\u6848\u8c19\u57ef\u63de\u72b4\u5eb5\u6849\u94f5\u9e4c\u9878\u9eef',
|
||||
'ang': '\u80ae\u6602\u76ce',
|
||||
'ao': '\u51f9\u6556\u71ac\u7ff1\u8884\u50b2\u5965\u61ca\u6fb3\u5773\u62d7\u55f7\u5662\u5c99\u5ed2\u9068\u5aaa\u9a9c\u8071\u87af\u93ca\u9ccc\u93d6',
|
||||
'ba': '\u82ad\u634c\u6252\u53ed\u5427\u7b06\u516b\u75a4\u5df4\u62d4\u8dcb\u9776\u628a\u8019\u575d\u9738\u7f62\u7238\u8307\u83dd\u8406\u636d\u5c9c\u705e\u6777\u94af\u7c91\u9c85\u9b43',
|
||||
'bai': '\u767d\u67cf\u767e\u6446\u4f70\u8d25\u62dc\u7a17\u859c\u63b0\u97b4',
|
||||
'ban': '\u6591\u73ed\u642c\u6273\u822c\u9881\u677f\u7248\u626e\u62cc\u4f34\u74e3\u534a\u529e\u7eca\u962a\u5742\u8c73\u94a3\u7622\u764d\u8228',
|
||||
'bang': '\u90a6\u5e2e\u6886\u699c\u8180\u7ed1\u68d2\u78c5\u868c\u9551\u508d\u8c24\u84a1\u8783',
|
||||
'bao': '\u82de\u80de\u5305\u8912\u96f9\u4fdd\u5821\u9971\u5b9d\u62b1\u62a5\u66b4\u8c79\u9c8d\u7206\u52f9\u8446\u5b80\u5b62\u7172\u9e28\u8913\u8db5\u9f85',
|
||||
'bo': '\u5265\u8584\u73bb\u83e0\u64ad\u62e8\u94b5\u6ce2\u535a\u52c3\u640f\u94c2\u7b94\u4f2f\u5e1b\u8236\u8116\u818a\u6e24\u6cca\u9a73\u4eb3\u8543\u5575\u997d\u6a97\u64d8\u7934\u94b9\u9e41\u7c38\u8ddb',
|
||||
'bei': '\u676f\u7891\u60b2\u5351\u5317\u8f88\u80cc\u8d1d\u94a1\u500d\u72c8\u5907\u60eb\u7119\u88ab\u5b5b\u9642\u90b6\u57e4\u84d3\u5457\u602b\u6096\u789a\u9e4e\u8919\u943e',
|
||||
'ben': '\u5954\u82ef\u672c\u7b28\u755a\u574c\u951b',
|
||||
'beng': '\u5d29\u7ef7\u752d\u6cf5\u8e66\u8ff8\u552a\u5623\u750f',
|
||||
'bi': '\u903c\u9f3b\u6bd4\u9119\u7b14\u5f7c\u78a7\u84d6\u853d\u6bd5\u6bd9\u6bd6\u5e01\u5e87\u75f9\u95ed\u655d\u5f0a\u5fc5\u8f9f\u58c1\u81c2\u907f\u965b\u5315\u4ef3\u4ffe\u8298\u835c\u8378\u5421\u54d4\u72f4\u5eb3\u610e\u6ed7\u6fde\u5f3c\u59a3\u5a62\u5b16\u74a7\u8d32\u7540\u94cb\u79d5\u88e8\u7b5a\u7b85\u7be6\u822d\u895e\u8df8\u9ac0',
|
||||
'bian': '\u97ad\u8fb9\u7f16\u8d2c\u6241\u4fbf\u53d8\u535e\u8fa8\u8fa9\u8fab\u904d\u533e\u5f01\u82c4\u5fed\u6c74\u7f0f\u7178\u782d\u78a5\u7a39\u7a86\u8759\u7b3e\u9cca',
|
||||
'biao': '\u6807\u5f6a\u8198\u8868\u5a4a\u9aa0\u98d1\u98d9\u98da\u706c\u9556\u9573\u762d\u88f1\u9cd4',
|
||||
'bie': '\u9cd6\u618b\u522b\u762a\u8e69\u9cd8',
|
||||
'bin': '\u5f6c\u658c\u6fd2\u6ee8\u5bbe\u6448\u50a7\u6d5c\u7f24\u73a2\u6ba1\u8191\u9554\u9acc\u9b13',
|
||||
'bing': '\u5175\u51b0\u67c4\u4e19\u79c9\u997c\u70b3\u75c5\u5e76\u7980\u90b4\u6452\u7ee0\u678b\u69df\u71f9',
|
||||
'bu': '\u6355\u535c\u54fa\u8865\u57e0\u4e0d\u5e03\u6b65\u7c3f\u90e8\u6016\u62ca\u535f\u900b\u74ff\u6661\u949a\u91ad',
|
||||
'ca': '\u64e6\u5693\u7924',
|
||||
'cai': '\u731c\u88c1\u6750\u624d\u8d22\u776c\u8e29\u91c7\u5f69\u83dc\u8521',
|
||||
'can': '\u9910\u53c2\u8695\u6b8b\u60ed\u60e8\u707f\u9a96\u74a8\u7cb2\u9eea',
|
||||
'cang': '\u82cd\u8231\u4ed3\u6ca7\u85cf\u4f27',
|
||||
'cao': '\u64cd\u7cd9\u69fd\u66f9\u8349\u8279\u5608\u6f15\u87ac\u825a',
|
||||
'ce': '\u5395\u7b56\u4fa7\u518c\u6d4b\u5202\u5e3b\u607b',
|
||||
'ceng': '\u5c42\u8e6d\u564c',
|
||||
'cha': '\u63d2\u53c9\u832c\u8336\u67e5\u78b4\u643d\u5bdf\u5c94\u5dee\u8be7\u7339\u9987\u6c4a\u59f9\u6748\u6942\u69ce\u6aab\u9497\u9538\u9572\u8869',
|
||||
'chai': '\u62c6\u67f4\u8c7a\u4faa\u8308\u7625\u867f\u9f87',
|
||||
'chan': '\u6400\u63ba\u8749\u998b\u8c17\u7f20\u94f2\u4ea7\u9610\u98a4\u5181\u8c04\u8c36\u8487\u5edb\u5fcf\u6f7a\u6fb6\u5b71\u7fbc\u5a75\u5b17\u9aa3\u89c7\u7985\u9561\u88e3\u87fe\u8e94',
|
||||
'chang': '\u660c\u7316\u573a\u5c1d\u5e38\u957f\u507f\u80a0\u5382\u655e\u7545\u5531\u5021\u4f25\u9b2f\u82cc\u83d6\u5f9c\u6005\u60dd\u960a\u5a3c\u5ae6\u6636\u6c05\u9cb3',
|
||||
'chao': '\u8d85\u6284\u949e\u671d\u5632\u6f6e\u5de2\u5435\u7092\u600a\u7ec9\u6641\u8016',
|
||||
'che': '\u8f66\u626f\u64a4\u63a3\u5f7b\u6f88\u577c\u5c6e\u7817',
|
||||
'chen': '\u90f4\u81e3\u8fb0\u5c18\u6668\u5ff1\u6c89\u9648\u8d81\u886c\u79f0\u8c0c\u62bb\u55d4\u5bb8\u741b\u6987\u809c\u80c2\u789c\u9f80',
|
||||
'cheng': '\u6491\u57ce\u6a59\u6210\u5448\u4e58\u7a0b\u60e9\u6f84\u8bda\u627f\u901e\u9a8b\u79e4\u57d5\u5d4a\u5fb5\u6d48\u67a8\u67fd\u6a18\u665f\u584d\u77a0\u94d6\u88ce\u86cf\u9172',
|
||||
'chi': '\u5403\u75f4\u6301\u5319\u6c60\u8fdf\u5f1b\u9a70\u803b\u9f7f\u4f88\u5c3a\u8d64\u7fc5\u65a5\u70bd\u50ba\u5880\u82aa\u830c\u640b\u53f1\u54e7\u557b\u55e4\u5f73\u996c\u6cb2\u5ab8\u6555\u80dd\u7719\u7735\u9e31\u761b\u892b\u86a9\u87ad\u7b1e\u7bea\u8c49\u8e05\u8e1f\u9b51',
|
||||
'chong': '\u5145\u51b2\u866b\u5d07\u5ba0\u833a\u5fe1\u61a7\u94f3\u825f',
|
||||
'chou': '\u62bd\u916c\u7574\u8e0c\u7a20\u6101\u7b79\u4ec7\u7ef8\u7785\u4e11\u4fe6\u5733\u5e31\u60c6\u6eb4\u59af\u7633\u96e0\u9c8b',
|
||||
'chu': '\u81ed\u521d\u51fa\u6a71\u53a8\u8e87\u9504\u96cf\u6ec1\u9664\u695a\u7840\u50a8\u77d7\u6410\u89e6\u5904\u4e8d\u520d\u61b7\u7ecc\u6775\u696e\u6a17\u870d\u8e70\u9edc',
|
||||
'chuan': '\u63e3\u5ddd\u7a7f\u693d\u4f20\u8239\u5598\u4e32\u63be\u821b\u60f4\u9044\u5ddb\u6c1a\u948f\u9569\u8221',
|
||||
'chuang': '\u75ae\u7a97\u5e62\u5e8a\u95ef\u521b\u6006',
|
||||
'chui': '\u5439\u708a\u6376\u9524\u5782\u9672\u68f0\u69cc',
|
||||
'chun': '\u6625\u693f\u9187\u5507\u6df3\u7eaf\u8822\u4fc3\u83bc\u6c8c\u80ab\u6710\u9e51\u877d',
|
||||
'chuo': '\u6233\u7ef0\u851f\u8fb6\u8f8d\u955e\u8e14\u9f8a',
|
||||
'ci': '\u75b5\u8328\u78c1\u96cc\u8f9e\u6148\u74f7\u8bcd\u6b64\u523a\u8d50\u6b21\u8360\u5472\u5d6f\u9e5a\u8785\u7ccd\u8d91',
|
||||
'cong': '\u806a\u8471\u56f1\u5306\u4ece\u4e1b\u506c\u82c1\u6dd9\u9aa2\u742e\u7481\u679e',
|
||||
'cu': '\u51d1\u7c97\u918b\u7c07\u731d\u6b82\u8e59',
|
||||
'cuan': '\u8e7f\u7be1\u7a9c\u6c46\u64ba\u6615\u7228',
|
||||
'cui': '\u6467\u5d14\u50ac\u8106\u7601\u7cb9\u6dec\u7fe0\u8403\u60b4\u7480\u69b1\u96b9',
|
||||
'cun': '\u6751\u5b58\u5bf8\u78cb\u5fd6\u76b4',
|
||||
'cuo': '\u64ae\u6413\u63aa\u632b\u9519\u539d\u811e\u9509\u77ec\u75e4\u9e7e\u8e49\u8e9c',
|
||||
'da': '\u642d\u8fbe\u7b54\u7629\u6253\u5927\u8037\u54d2\u55d2\u601b\u59b2\u75b8\u8921\u7b2a\u977c\u9791',
|
||||
'dai': '\u5446\u6b79\u50a3\u6234\u5e26\u6b86\u4ee3\u8d37\u888b\u5f85\u902e\u6020\u57ed\u7519\u5454\u5cb1\u8fe8\u902f\u9a80\u7ed0\u73b3\u9edb',
|
||||
'dan': '\u803d\u62c5\u4e39\u5355\u90f8\u63b8\u80c6\u65e6\u6c2e\u4f46\u60ee\u6de1\u8bde\u5f39\u86cb\u4ebb\u510b\u5369\u840f\u5556\u6fb9\u6a90\u6b9a\u8d55\u7708\u7605\u8043\u7baa',
|
||||
'dang': '\u5f53\u6321\u515a\u8361\u6863\u8c20\u51fc\u83ea\u5b95\u7800\u94db\u88c6',
|
||||
'dao': '\u5200\u6363\u8e48\u5012\u5c9b\u7977\u5bfc\u5230\u7a3b\u60bc\u9053\u76d7\u53e8\u5541\u5fc9\u6d2e\u6c18\u7118\u5fd1\u7e9b',
|
||||
'de': '\u5fb7\u5f97\u7684\u951d',
|
||||
'deng': '\u8e6c\u706f\u767b\u7b49\u77aa\u51f3\u9093\u5654\u5d9d\u6225\u78f4\u956b\u7c26',
|
||||
'di': '\u5824\u4f4e\u6ef4\u8fea\u654c\u7b1b\u72c4\u6da4\u7fdf\u5ae1\u62b5\u5e95\u5730\u8482\u7b2c\u5e1d\u5f1f\u9012\u7f14\u6c10\u7c74\u8bcb\u8c1b\u90b8\u577b\u839c\u837b\u5600\u5a23\u67e2\u68e3\u89cc\u7825\u78b2\u7747\u955d\u7f9d\u9ab6',
|
||||
'dian': '\u98a0\u6382\u6ec7\u7898\u70b9\u5178\u975b\u57ab\u7535\u4f43\u7538\u5e97\u60e6\u5960\u6dc0\u6bbf\u4e36\u963d\u576b\u57dd\u5dc5\u73b7\u765c\u766b\u7c1f\u8e2e',
|
||||
'diao': '\u7889\u53fc\u96d5\u51cb\u5201\u6389\u540a\u9493\u8c03\u8f7a\u94de\u8729\u7c9c\u8c82',
|
||||
'die': '\u8dcc\u7239\u789f\u8776\u8fed\u8c0d\u53e0\u4f5a\u57a4\u581e\u63f2\u558b\u6e2b\u8f76\u7252\u74de\u8936\u800b\u8e40\u9cbd\u9cce',
|
||||
'ding': '\u4e01\u76ef\u53ee\u9489\u9876\u9f0e\u952d\u5b9a\u8ba2\u4e22\u4ec3\u5576\u738e\u815a\u7887\u753a\u94e4\u7594\u8035\u914a',
|
||||
'dong': '\u4e1c\u51ac\u8463\u61c2\u52a8\u680b\u4f97\u606b\u51bb\u6d1e\u578c\u549a\u5cbd\u5cd2\u5902\u6c21\u80e8\u80f4\u7850\u9e2b',
|
||||
'dou': '\u515c\u6296\u6597\u9661\u8c46\u9017\u75d8\u8538\u94ad\u7aa6\u7aac\u86aa\u7bfc\u9161',
|
||||
'du': '\u90fd\u7763\u6bd2\u728a\u72ec\u8bfb\u5835\u7779\u8d4c\u675c\u9540\u809a\u5ea6\u6e21\u5992\u828f\u561f\u6e0e\u691f\u6a50\u724d\u8839\u7b03\u9ad1\u9ee9',
|
||||
'duan': '\u7aef\u77ed\u953b\u6bb5\u65ad\u7f0e\u5f56\u6934\u7145\u7c16',
|
||||
'dui': '\u5806\u5151\u961f\u5bf9\u603c\u619d\u7893',
|
||||
'dun': '\u58a9\u5428\u8e72\u6566\u987f\u56e4\u949d\u76fe\u9041\u7096\u7818\u7905\u76f9\u9566\u8db8',
|
||||
'duo': '\u6387\u54c6\u591a\u593a\u579b\u8eb2\u6735\u8dfa\u8235\u5241\u60f0\u5815\u5484\u54da\u7f0d\u67c1\u94ce\u88f0\u8e31',
|
||||
'e': '\u86fe\u5ce8\u9e45\u4fc4\u989d\u8bb9\u5a25\u6076\u5384\u627c\u904f\u9102\u997f\u5669\u8c14\u57a9\u57ad\u82ca\u83aa\u843c\u5443\u6115\u5c59\u5a40\u8f6d\u66f7\u816d\u786a\u9507\u9537\u9e57\u989a\u9cc4',
|
||||
'en': '\u6069\u84bd\u6441\u5514\u55ef',
|
||||
'er': '\u800c\u513f\u8033\u5c14\u9975\u6d31\u4e8c\u8d30\u8fe9\u73e5\u94d2\u9e38\u9c95',
|
||||
'fa': '\u53d1\u7f5a\u7b4f\u4f10\u4e4f\u9600\u6cd5\u73d0\u57a1\u781d',
|
||||
'fan': '\u85e9\u5e06\u756a\u7ffb\u6a0a\u77fe\u9492\u7e41\u51e1\u70e6\u53cd\u8fd4\u8303\u8d29\u72af\u996d\u6cdb\u8629\u5e61\u72ad\u68b5\u6535\u71d4\u7548\u8e6f',
|
||||
'fang': '\u574a\u82b3\u65b9\u80aa\u623f\u9632\u59a8\u4eff\u8bbf\u7eba\u653e\u531a\u90a1\u5f77\u94ab\u822b\u9c82',
|
||||
'fei': '\u83f2\u975e\u5561\u98de\u80a5\u532a\u8bfd\u5420\u80ba\u5e9f\u6cb8\u8d39\u82be\u72d2\u60b1\u6ddd\u5983\u7ecb\u7eef\u69a7\u8153\u6590\u6249\u7953\u7829\u9544\u75f1\u871a\u7bda\u7fe1\u970f\u9cb1',
|
||||
'fen': '\u82ac\u915a\u5429\u6c1b\u5206\u7eb7\u575f\u711a\u6c7e\u7c89\u594b\u4efd\u5fff\u6124\u7caa\u507e\u7035\u68fc\u610d\u9cbc\u9f22',
|
||||
'feng': '\u4e30\u5c01\u67ab\u8702\u5cf0\u950b\u98ce\u75af\u70fd\u9022\u51af\u7f1d\u8bbd\u5949\u51e4\u4ff8\u9146\u8451\u6ca3\u781c',
|
||||
'fu': '\u4f5b\u5426\u592b\u6577\u80a4\u5b75\u6276\u62c2\u8f90\u5e45\u6c1f\u7b26\u4f0f\u4fd8\u670d\u6d6e\u6daa\u798f\u88b1\u5f17\u752b\u629a\u8f85\u4fef\u91dc\u65a7\u812f\u8151\u5e9c\u8150\u8d74\u526f\u8986\u8d4b\u590d\u5085\u4ed8\u961c\u7236\u8179\u8d1f\u5bcc\u8ba3\u9644\u5987\u7f1a\u5490\u5310\u51eb\u90db\u8299\u82fb\u832f\u83a9\u83d4\u544b\u5e5e\u6ecf\u8274\u5b5a\u9a78\u7ec2\u6874\u8d59\u9efb\u9efc\u7f58\u7a03\u99a5\u864d\u86a8\u8709\u8760\u876e\u9eb8\u8dba\u8dd7\u9cc6',
|
||||
'ga': '\u5676\u560e\u86e4\u5c2c\u5477\u5c15\u5c1c\u65ee\u9486',
|
||||
'gai': '\u8be5\u6539\u6982\u9499\u76d6\u6e89\u4e10\u9654\u5793\u6224\u8d45\u80f2',
|
||||
'gan': '\u5e72\u7518\u6746\u67d1\u7aff\u809d\u8d76\u611f\u79c6\u6562\u8d63\u5769\u82f7\u5c34\u64c0\u6cd4\u6de6\u6f89\u7ec0\u6a44\u65f0\u77f8\u75b3\u9150',
|
||||
'gang': '\u5188\u521a\u94a2\u7f38\u809b\u7eb2\u5c97\u6e2f\u6206\u7f61\u9883\u7b7b',
|
||||
'gong': '\u6760\u5de5\u653b\u529f\u606d\u9f9a\u4f9b\u8eac\u516c\u5bab\u5f13\u5de9\u6c5e\u62f1\u8d21\u5171\u857b\u5efe\u54a3\u73d9\u80b1\u86a3\u86e9\u89e5',
|
||||
'gao': '\u7bd9\u768b\u9ad8\u818f\u7f94\u7cd5\u641e\u9550\u7a3f\u544a\u777e\u8bf0\u90dc\u84bf\u85c1\u7f1f\u69d4\u69c1\u6772\u9506',
|
||||
'ge': '\u54e5\u6b4c\u6401\u6208\u9e3d\u80f3\u7599\u5272\u9769\u845b\u683c\u9601\u9694\u94ec\u4e2a\u5404\u9b32\u4ee1\u54ff\u5865\u55dd\u7ea5\u643f\u8188\u784c\u94ea\u9549\u88bc\u988c\u867c\u8238\u9abc\u9ac2',
|
||||
'gei': '\u7ed9',
|
||||
'gen': '\u6839\u8ddf\u4e98\u831b\u54cf\u826e',
|
||||
'geng': '\u8015\u66f4\u5e9a\u7fb9\u57c2\u803f\u6897\u54fd\u8d53\u9ca0',
|
||||
'gou': '\u94a9\u52fe\u6c9f\u82df\u72d7\u57a2\u6784\u8d2d\u591f\u4f5d\u8bdf\u5ca3\u9058\u5abe\u7f11\u89cf\u5f40\u9e32\u7b31\u7bdd\u97b2',
|
||||
'gu': '\u8f9c\u83c7\u5495\u7b8d\u4f30\u6cbd\u5b64\u59d1\u9f13\u53e4\u86ca\u9aa8\u8c37\u80a1\u6545\u987e\u56fa\u96c7\u560f\u8bc2\u83f0\u54cc\u5d2e\u6c69\u688f\u8f71\u726f\u727f\u80cd\u81cc\u6bc2\u77bd\u7f5f\u94b4\u9522\u74e0\u9e2a\u9e44\u75fc\u86c4\u9164\u89da\u9cb4\u9ab0\u9e58',
|
||||
'gua': '\u522e\u74dc\u5250\u5be1\u6302\u8902\u5366\u8bd6\u5471\u681d\u9e39',
|
||||
'guai': '\u4e56\u62d0\u602a\u54d9',
|
||||
'guan': '\u68fa\u5173\u5b98\u51a0\u89c2\u7ba1\u9986\u7f50\u60ef\u704c\u8d2f\u500c\u839e\u63bc\u6dab\u76e5\u9e73\u9ccf',
|
||||
'guang': '\u5149\u5e7f\u901b\u72b7\u6844\u80f1\u7592',
|
||||
'gui': '\u7470\u89c4\u572d\u7845\u5f52\u9f9f\u95fa\u8f68\u9b3c\u8be1\u7678\u6842\u67dc\u8dea\u8d35\u523d\u5326\u523f\u5e8b\u5b84\u59ab\u6867\u7085\u6677\u7688\u7c0b\u9c91\u9cdc',
|
||||
'gun': '\u8f8a\u6eda\u68cd\u4e28\u886e\u7ef2\u78d9\u9ca7',
|
||||
'guo': '\u9505\u90ed\u56fd\u679c\u88f9\u8fc7\u9998\u8803\u57da\u63b4\u5459\u56d7\u5e3c\u5d1e\u7313\u6901\u8662\u951e\u8052\u872e\u873e\u8748',
|
||||
'ha': '\u54c8',
|
||||
'hai': '\u9ab8\u5b69\u6d77\u6c26\u4ea5\u5bb3\u9a87\u54b4\u55e8\u988f\u91a2',
|
||||
'han': '\u9163\u61a8\u90af\u97e9\u542b\u6db5\u5bd2\u51fd\u558a\u7f55\u7ff0\u64bc\u634d\u65f1\u61be\u608d\u710a\u6c57\u6c49\u9097\u83e1\u6496\u961a\u701a\u6657\u7113\u9894\u86b6\u9f3e',
|
||||
'hen': '\u592f\u75d5\u5f88\u72e0\u6068',
|
||||
'hang': '\u676d\u822a\u6c86\u7ed7\u73e9\u6841',
|
||||
'hao': '\u58d5\u568e\u8c6a\u6beb\u90dd\u597d\u8017\u53f7\u6d69\u8585\u55e5\u5686\u6fe0\u704f\u660a\u7693\u98a2\u869d',
|
||||
'he': '\u5475\u559d\u8377\u83cf\u6838\u79be\u548c\u4f55\u5408\u76d2\u8c89\u9602\u6cb3\u6db8\u8d6b\u8910\u9e64\u8d3a\u8bc3\u52be\u58d1\u85ff\u55d1\u55ec\u9616\u76cd\u86b5\u7fee',
|
||||
'hei': '\u563f\u9ed1',
|
||||
'heng': '\u54fc\u4ea8\u6a2a\u8861\u6052\u8a07\u8605',
|
||||
'hong': '\u8f70\u54c4\u70d8\u8679\u9e3f\u6d2a\u5b8f\u5f18\u7ea2\u9ec9\u8ba7\u836d\u85a8\u95f3\u6cd3',
|
||||
'hou': '\u5589\u4faf\u7334\u543c\u539a\u5019\u540e\u5820\u5f8c\u9005\u760a\u7bcc\u7cc7\u9c8e\u9aba',
|
||||
'hu': '\u547c\u4e4e\u5ffd\u745a\u58f6\u846b\u80e1\u8774\u72d0\u7cca\u6e56\u5f27\u864e\u552c\u62a4\u4e92\u6caa\u6237\u51b1\u553f\u56eb\u5cb5\u7322\u6019\u60da\u6d52\u6ef9\u7425\u69f2\u8f77\u89f3\u70c0\u7173\u623d\u6248\u795c\u9e55\u9e71\u7b0f\u9190\u659b',
|
||||
'hua': '\u82b1\u54d7\u534e\u733e\u6ed1\u753b\u5212\u5316\u8bdd\u5290\u6d4d\u9a85\u6866\u94e7\u7a1e',
|
||||
'huai': '\u69d0\u5f8a\u6000\u6dee\u574f\u8fd8\u8e1d',
|
||||
'huan': '\u6b22\u73af\u6853\u7f13\u6362\u60a3\u5524\u75ea\u8c62\u7115\u6da3\u5ba6\u5e7b\u90c7\u5942\u57b8\u64d0\u571c\u6d39\u6d63\u6f36\u5bf0\u902d\u7f33\u953e\u9ca9\u9b1f',
|
||||
'huang': '\u8352\u614c\u9ec4\u78fa\u8757\u7c27\u7687\u51f0\u60f6\u714c\u6643\u5e4c\u604d\u8c0e\u968d\u5fa8\u6e5f\u6f62\u9051\u749c\u8093\u7640\u87e5\u7bc1\u9cc7',
|
||||
'hui': '\u7070\u6325\u8f89\u5fbd\u6062\u86d4\u56de\u6bc1\u6094\u6167\u5349\u60e0\u6666\u8d3f\u79fd\u4f1a\u70e9\u6c47\u8bb3\u8bf2\u7ed8\u8bd9\u8334\u835f\u8559\u54d5\u5599\u96b3\u6d04\u5f57\u7f0b\u73f2\u6656\u605a\u867a\u87ea\u9ebe',
|
||||
'hun': '\u8364\u660f\u5a5a\u9b42\u6d51\u6df7\u8be8\u9984\u960d\u6eb7\u7f17',
|
||||
'huo': '\u8c41\u6d3b\u4f19\u706b\u83b7\u6216\u60d1\u970d\u8d27\u7978\u6509\u56af\u5925\u94ac\u952a\u956c\u8020\u8816',
|
||||
'ji': '\u51fb\u573e\u57fa\u673a\u7578\u7a3d\u79ef\u7b95\u808c\u9965\u8ff9\u6fc0\u8ba5\u9e21\u59ec\u7ee9\u7f09\u5409\u6781\u68d8\u8f91\u7c4d\u96c6\u53ca\u6025\u75be\u6c72\u5373\u5ac9\u7ea7\u6324\u51e0\u810a\u5df1\u84df\u6280\u5180\u5b63\u4f0e\u796d\u5242\u60b8\u6d4e\u5bc4\u5bc2\u8ba1\u8bb0\u65e2\u5fcc\u9645\u5993\u7ee7\u7eaa\u5c45\u4e0c\u4e69\u525e\u4f76\u4f74\u8114\u58bc\u82a8\u82b0\u8401\u84ba\u857a\u638e\u53fd\u54ad\u54dc\u5527\u5c8c\u5d74\u6d0e\u5f50\u5c50\u9aa5\u757f\u7391\u696b\u6b9b\u621f\u6222\u8d4d\u89ca\u7284\u9f51\u77f6\u7f81\u5d47\u7a37\u7620\u7635\u866e\u7b08\u7b04\u66a8\u8dfb\u8dfd\u9701\u9c9a\u9cab\u9afb\u9e82',
|
||||
'jia': '\u5609\u67b7\u5939\u4f73\u5bb6\u52a0\u835a\u988a\u8d3e\u7532\u94be\u5047\u7a3c\u4ef7\u67b6\u9a7e\u5ac1\u4f3d\u90cf\u62ee\u5cac\u6d43\u8fe6\u73c8\u621b\u80db\u605d\u94d7\u9553\u75c2\u86f1\u7b33\u8888\u8dcf',
|
||||
'jian': '\u6b7c\u76d1\u575a\u5c16\u7b3a\u95f4\u714e\u517c\u80a9\u8270\u5978\u7f04\u8327\u68c0\u67ec\u78b1\u7877\u62e3\u6361\u7b80\u4fed\u526a\u51cf\u8350\u69db\u9274\u8df5\u8d31\u89c1\u952e\u7bad\u4ef6\u5065\u8230\u5251\u996f\u6e10\u6e85\u6da7\u5efa\u50ed\u8c0f\u8c2b\u83c5\u84b9\u641b\u56dd\u6e54\u8e47\u8b07\u7f23\u67a7\u67d9\u6957\u620b\u622c\u726e\u728d\u6bfd\u8171\u7751\u950f\u9e63\u88e5\u7b15\u7bb4\u7fe6\u8dbc\u8e3a\u9ca3\u97af',
|
||||
'jiang': '\u50f5\u59dc\u5c06\u6d46\u6c5f\u7586\u848b\u6868\u5956\u8bb2\u5320\u9171\u964d\u8333\u6d1a\u7edb\u7f30\u729f\u7913\u8029\u7ce8\u8c47',
|
||||
'jiao': '\u8549\u6912\u7901\u7126\u80f6\u4ea4\u90ca\u6d47\u9a84\u5a07\u56bc\u6405\u94f0\u77eb\u4fa5\u811a\u72e1\u89d2\u997a\u7f34\u7ede\u527f\u6559\u9175\u8f7f\u8f83\u53eb\u4f7c\u50ec\u832d\u6322\u564d\u5ce4\u5fbc\u59e3\u7e9f\u656b\u768e\u9e6a\u86df\u91ae\u8de4\u9c9b',
|
||||
'jie': '\u7a96\u63ed\u63a5\u7686\u79f8\u8857\u9636\u622a\u52ab\u8282\u6854\u6770\u6377\u776b\u7aed\u6d01\u7ed3\u89e3\u59d0\u6212\u85c9\u82a5\u754c\u501f\u4ecb\u75a5\u8beb\u5c4a\u5048\u8ba6\u8bd8\u5588\u55df\u736c\u5a55\u5b51\u6840\u7352\u78a3\u9534\u7596\u88b7\u9889\u86a7\u7faf\u9c92\u9ab1\u9aeb',
|
||||
'jin': '\u5dfe\u7b4b\u65a4\u91d1\u4eca\u6d25\u895f\u7d27\u9526\u4ec5\u8c28\u8fdb\u9773\u664b\u7981\u8fd1\u70ec\u6d78\u5c3d\u537a\u8369\u5807\u5664\u9991\u5ed1\u5997\u7f19\u747e\u69ff\u8d46\u89d0\u9485\u9513\u887f\u77dc',
|
||||
'jing': '\u52b2\u8346\u5162\u830e\u775b\u6676\u9cb8\u4eac\u60ca\u7cbe\u7cb3\u7ecf\u4e95\u8b66\u666f\u9888\u9759\u5883\u656c\u955c\u5f84\u75c9\u9756\u7adf\u7ade\u51c0\u522d\u5106\u9631\u83c1\u734d\u61ac\u6cfe\u8ff3\u5f2a\u5a67\u80bc\u80eb\u8148\u65cc',
|
||||
'jiong': '\u70af\u7a98\u5182\u8fe5\u6243',
|
||||
'jiu': '\u63ea\u7a76\u7ea0\u7396\u97ed\u4e45\u7078\u4e5d\u9152\u53a9\u6551\u65e7\u81fc\u8205\u548e\u5c31\u759a\u50e6\u557e\u9604\u67e9\u6855\u9e6b\u8d73\u9b0f',
|
||||
'ju': '\u97a0\u62d8\u72d9\u75bd\u9a79\u83ca\u5c40\u5480\u77e9\u4e3e\u6cae\u805a\u62d2\u636e\u5de8\u5177\u8ddd\u8e1e\u952f\u4ff1\u53e5\u60e7\u70ac\u5267\u5028\u8bb5\u82e3\u82f4\u8392\u63ac\u907d\u5c66\u741a\u67b8\u6910\u6998\u6989\u6a58\u728b\u98d3\u949c\u9514\u7aad\u88fe\u8d84\u91b5\u8e3d\u9f83\u96ce\u97ab',
|
||||
'juan': '\u6350\u9e43\u5a1f\u5026\u7737\u5377\u7ee2\u9104\u72f7\u6d93\u684a\u8832\u9529\u954c\u96bd',
|
||||
'jue': '\u6485\u652b\u6289\u6398\u5014\u7235\u89c9\u51b3\u8bc0\u7edd\u53a5\u5282\u8c32\u77cd\u8568\u5658\u5d1b\u7357\u5b53\u73cf\u6877\u6a5b\u721d\u9562\u8e76\u89d6',
|
||||
'jun': '\u5747\u83cc\u94a7\u519b\u541b\u5cfb\u4fca\u7ae3\u6d5a\u90e1\u9a8f\u6343\u72fb\u76b2\u7b60\u9e87',
|
||||
'ka': '\u5580\u5496\u5361\u4f67\u5494\u80e9',
|
||||
'ke': '\u54af\u5777\u82db\u67ef\u68f5\u78d5\u9897\u79d1\u58f3\u54b3\u53ef\u6e34\u514b\u523b\u5ba2\u8bfe\u5ca2\u606a\u6e98\u9a92\u7f02\u73c2\u8f72\u6c2a\u778c\u94b6\u75b4\u7aa0\u874c\u9ac1',
|
||||
'kai': '\u5f00\u63e9\u6977\u51ef\u6168\u5240\u57b2\u8488\u5ffe\u607a\u94e0\u950e',
|
||||
'kan': '\u520a\u582a\u52d8\u574e\u780d\u770b\u4f83\u51f5\u83b0\u83b6\u6221\u9f9b\u77b0',
|
||||
'kang': '\u5eb7\u6177\u7ce0\u625b\u6297\u4ea2\u7095\u5751\u4f09\u95f6\u94aa',
|
||||
'kao': '\u8003\u62f7\u70e4\u9760\u5c3b\u6832\u7292\u94d0',
|
||||
'ken': '\u80af\u5543\u57a6\u6073\u57a0\u88c9\u9880',
|
||||
'keng': '\u542d\u5fd0\u94ff',
|
||||
'kong': '\u7a7a\u6050\u5b54\u63a7\u5025\u5d06\u7b9c',
|
||||
'kou': '\u62a0\u53e3\u6263\u5bc7\u82a4\u853b\u53e9\u770d\u7b58',
|
||||
'ku': '\u67af\u54ed\u7a9f\u82e6\u9177\u5e93\u88e4\u5233\u5800\u55be\u7ed4\u9ab7',
|
||||
'kua': '\u5938\u57ae\u630e\u8de8\u80ef\u4f89',
|
||||
'kuai': '\u5757\u7b77\u4fa9\u5feb\u84af\u90d0\u8489\u72ef\u810d',
|
||||
'kuan': '\u5bbd\u6b3e\u9acb',
|
||||
'kuang': '\u5321\u7b50\u72c2\u6846\u77ff\u7736\u65f7\u51b5\u8bd3\u8bf3\u909d\u5739\u593c\u54d0\u7ea9\u8d36',
|
||||
'kui': '\u4e8f\u76d4\u5cbf\u7aa5\u8475\u594e\u9b41\u5080\u9988\u6127\u6e83\u9997\u532e\u5914\u9697\u63c6\u55b9\u559f\u609d\u6126\u9615\u9035\u668c\u777d\u8069\u8770\u7bd1\u81fe\u8dec',
|
||||
'kun': '\u5764\u6606\u6346\u56f0\u6083\u9603\u7428\u951f\u918c\u9cb2\u9ae1',
|
||||
'kuo': '\u62ec\u6269\u5ed3\u9614\u86de',
|
||||
'la': '\u5783\u62c9\u5587\u8721\u814a\u8fa3\u5566\u524c\u647a\u908b\u65ef\u782c\u760c',
|
||||
'lai': '\u83b1\u6765\u8d56\u5d03\u5f95\u6d9e\u6fd1\u8d49\u7750\u94fc\u765e\u7c41',
|
||||
'lan': '\u84dd\u5a6a\u680f\u62e6\u7bee\u9611\u5170\u6f9c\u8c30\u63fd\u89c8\u61d2\u7f06\u70c2\u6ee5\u5549\u5c9a\u61d4\u6f24\u6984\u6593\u7f71\u9567\u8934',
|
||||
'lang': '\u7405\u6994\u72fc\u5eca\u90ce\u6717\u6d6a\u83a8\u8497\u5577\u9606\u9512\u7a02\u8782',
|
||||
'lao': '\u635e\u52b3\u7262\u8001\u4f6c\u59e5\u916a\u70d9\u6d9d\u5520\u5d02\u6833\u94d1\u94f9\u75e8\u91aa',
|
||||
'le': '\u52d2\u4e50\u808b\u4ec2\u53fb\u561e\u6cd0\u9cd3',
|
||||
'lei': '\u96f7\u956d\u857e\u78ca\u7d2f\u5121\u5792\u64c2\u7c7b\u6cea\u7fb8\u8bd4\u837d\u54a7\u6f2f\u5ad8\u7f27\u6a91\u8012\u9179',
|
||||
'ling': '\u68f1\u51b7\u62ce\u73b2\u83f1\u96f6\u9f84\u94c3\u4f36\u7f9a\u51cc\u7075\u9675\u5cad\u9886\u53e6\u4ee4\u9143\u5844\u82d3\u5464\u56f9\u6ce0\u7eeb\u67c3\u68c2\u74f4\u8046\u86c9\u7fce\u9cae',
|
||||
'leng': '\u695e\u6123',
|
||||
'li': '\u5398\u68a8\u7281\u9ece\u7bf1\u72f8\u79bb\u6f13\u7406\u674e\u91cc\u9ca4\u793c\u8389\u8354\u540f\u6817\u4e3d\u5389\u52b1\u783e\u5386\u5229\u5088\u4f8b\u4fd0\u75e2\u7acb\u7c92\u6ca5\u96b6\u529b\u7483\u54e9\u4fea\u4fda\u90e6\u575c\u82c8\u8385\u84e0\u85dc\u6369\u5456\u5533\u55b1\u7301\u6ea7\u6fa7\u9026\u5a0c\u5ae0\u9a8a\u7f21\u73de\u67a5\u680e\u8f79\u623e\u783a\u8a48\u7f79\u9502\u9e42\u75a0\u75ac\u86ce\u870a\u8821\u7b20\u7be5\u7c9d\u91b4\u8dde\u96f3\u9ca1\u9ce2\u9ee7',
|
||||
'lian': '\u4fe9\u8054\u83b2\u8fde\u9570\u5ec9\u601c\u6d9f\u5e18\u655b\u8138\u94fe\u604b\u70bc\u7ec3\u631b\u8539\u5941\u6f4b\u6fc2\u5a08\u740f\u695d\u6b93\u81c1\u81a6\u88e2\u880a\u9ca2',
|
||||
'liang': '\u7cae\u51c9\u6881\u7cb1\u826f\u4e24\u8f86\u91cf\u667e\u4eae\u8c05\u589a\u690b\u8e09\u9753\u9b49',
|
||||
'liao': '\u64a9\u804a\u50da\u7597\u71ce\u5be5\u8fbd\u6f66\u4e86\u6482\u9563\u5ed6\u6599\u84fc\u5c25\u5639\u7360\u5bee\u7f2d\u948c\u9e69\u8022',
|
||||
'lie': '\u5217\u88c2\u70c8\u52a3\u730e\u51bd\u57d2\u6d0c\u8d94\u8e90\u9b23',
|
||||
'lin': '\u7433\u6797\u78f7\u9716\u4e34\u90bb\u9cde\u6dcb\u51db\u8d41\u541d\u853a\u5d99\u5eea\u9074\u6aa9\u8f9a\u77b5\u7cbc\u8e8f\u9e9f',
|
||||
'liu': '\u6e9c\u7409\u69b4\u786b\u998f\u7559\u5218\u7624\u6d41\u67f3\u516d\u62a1\u507b\u848c\u6cd6\u6d4f\u905b\u9a9d\u7efa\u65d2\u7198\u950d\u954f\u9e68\u938f',
|
||||
'long': '\u9f99\u804b\u5499\u7b3c\u7abf\u9686\u5784\u62e2\u9647\u5f04\u5785\u830f\u6cf7\u73d1\u680a\u80e7\u783b\u7643',
|
||||
'lou': '\u697c\u5a04\u6402\u7bd3\u6f0f\u964b\u55bd\u5d5d\u9542\u7618\u8027\u877c\u9ac5',
|
||||
'lu': '\u82a6\u5362\u9885\u5e90\u7089\u63b3\u5364\u864f\u9c81\u9e93\u788c\u9732\u8def\u8d42\u9e7f\u6f5e\u7984\u5f55\u9646\u622e\u5786\u6445\u64b8\u565c\u6cf8\u6e0c\u6f09\u7490\u680c\u6a79\u8f73\u8f82\u8f98\u6c07\u80ea\u9565\u9e2c\u9e6d\u7c0f\u823b\u9c88',
|
||||
'lv': '\u9a74\u5415\u94dd\u4fa3\u65c5\u5c65\u5c61\u7f15\u8651\u6c2f\u5f8b\u7387\u6ee4\u7eff\u634b\u95fe\u6988\u8182\u7a06\u891b',
|
||||
'luan': '\u5ce6\u5b6a\u6ee6\u5375\u4e71\u683e\u9e3e\u92ae',
|
||||
'lue': '\u63a0\u7565\u950a',
|
||||
'lun': '\u8f6e\u4f26\u4ed1\u6ca6\u7eb6\u8bba\u56f5',
|
||||
'luo': '\u841d\u87ba\u7f57\u903b\u9523\u7ba9\u9aa1\u88f8\u843d\u6d1b\u9a86\u7edc\u502e\u8366\u645e\u7321\u6cfa\u6924\u8136\u9559\u7630\u96d2',
|
||||
'ma': '\u5988\u9ebb\u739b\u7801\u8682\u9a6c\u9a82\u561b\u5417\u551b\u72b8\u5b37\u6769\u9ebd',
|
||||
'mai': '\u57cb\u4e70\u9ea6\u5356\u8fc8\u8109\u52a2\u836c\u54aa\u973e',
|
||||
'man': '\u7792\u9992\u86ee\u6ee1\u8513\u66fc\u6162\u6f2b\u8c29\u5881\u5e54\u7f26\u71b3\u9558\u989f\u87a8\u9cd7\u9794',
|
||||
'mang': '\u8292\u832b\u76f2\u5fd9\u83bd\u9099\u6f2d\u6726\u786d\u87d2',
|
||||
'meng': '\u6c13\u840c\u8499\u6aac\u76df\u9530\u731b\u68a6\u5b5f\u52d0\u750d\u77a2\u61f5\u791e\u867b\u8722\u8813\u824b\u8268\u9efe',
|
||||
'miao': '\u732b\u82d7\u63cf\u7784\u85d0\u79d2\u6e3a\u5e99\u5999\u55b5\u9088\u7f08\u7f2a\u676a\u6dfc\u7707\u9e4b\u8731',
|
||||
'mao': '\u8305\u951a\u6bdb\u77db\u94c6\u536f\u8302\u5192\u5e3d\u8c8c\u8d38\u4f94\u88a4\u52d6\u8306\u5cc1\u7441\u6634\u7266\u8004\u65c4\u61cb\u7780\u86d1\u8765\u87ca\u9ae6',
|
||||
'me': '\u4e48',
|
||||
'mei': '\u73ab\u679a\u6885\u9176\u9709\u7164\u6ca1\u7709\u5a92\u9541\u6bcf\u7f8e\u6627\u5bd0\u59b9\u5a9a\u5776\u8393\u5d4b\u7338\u6d7c\u6e44\u6963\u9545\u9e5b\u8882\u9b45',
|
||||
'men': '\u95e8\u95f7\u4eec\u626a\u739f\u7116\u61d1\u9494',
|
||||
'mi': '\u772f\u919a\u9761\u7cdc\u8ff7\u8c1c\u5f25\u7c73\u79d8\u89c5\u6ccc\u871c\u5bc6\u5e42\u8288\u5196\u8c27\u863c\u5627\u7315\u736f\u6c68\u5b93\u5f2d\u8112\u6549\u7cf8\u7e3b\u9e8b',
|
||||
'mian': '\u68c9\u7720\u7ef5\u5195\u514d\u52c9\u5a29\u7f05\u9762\u6c94\u6e4e\u817c\u7704',
|
||||
'mie': '\u8511\u706d\u54a9\u881b\u7bfe',
|
||||
'min': '\u6c11\u62bf\u76bf\u654f\u60af\u95fd\u82e0\u5cb7\u95f5\u6cef\u73c9',
|
||||
'ming': '\u660e\u879f\u9e23\u94ed\u540d\u547d\u51a5\u8317\u6e9f\u669d\u7791\u9169',
|
||||
'miu': '\u8c2c',
|
||||
'mo': '\u6478\u6479\u8611\u6a21\u819c\u78e8\u6469\u9b54\u62b9\u672b\u83ab\u58a8\u9ed8\u6cab\u6f20\u5bde\u964c\u8c1f\u8309\u84e6\u998d\u5aeb\u9546\u79e3\u763c\u8031\u87c6\u8c8a\u8c98',
|
||||
'mou': '\u8c0b\u725f\u67d0\u53b6\u54de\u5a7a\u7738\u936a',
|
||||
'mu': '\u62c7\u7261\u4ea9\u59c6\u6bcd\u5893\u66ae\u5e55\u52df\u6155\u6728\u76ee\u7766\u7267\u7a46\u4eeb\u82dc\u5452\u6c90\u6bea\u94bc',
|
||||
'na': '\u62ff\u54ea\u5450\u94a0\u90a3\u5a1c\u7eb3\u5185\u637a\u80ad\u954e\u8872\u7bac',
|
||||
'nai': '\u6c16\u4e43\u5976\u8010\u5948\u9f10\u827f\u8418\u67f0',
|
||||
'nan': '\u5357\u7537\u96be\u56ca\u5583\u56e1\u6960\u8169\u877b\u8d67',
|
||||
'nao': '\u6320\u8111\u607c\u95f9\u5b6c\u57b4\u7331\u7459\u7847\u94d9\u86f2',
|
||||
'ne': '\u6dd6\u5462\u8bb7',
|
||||
'nei': '\u9981',
|
||||
'nen': '\u5ae9\u80fd\u6798\u6041',
|
||||
'ni': '\u59ae\u9713\u502a\u6ce5\u5c3c\u62df\u4f60\u533f\u817b\u9006\u6eba\u4f32\u576d\u730a\u6029\u6ee0\u6635\u65ce\u7962\u615d\u7768\u94cc\u9cb5',
|
||||
'nian': '\u852b\u62c8\u5e74\u78be\u64b5\u637b\u5ff5\u5eff\u8f87\u9ecf\u9c87\u9cb6',
|
||||
'niang': '\u5a18\u917f',
|
||||
'niao': '\u9e1f\u5c3f\u8311\u5b32\u8132\u8885',
|
||||
'nie': '\u634f\u8042\u5b7d\u556e\u954a\u954d\u6d85\u4e5c\u9667\u8616\u55eb\u8080\u989e\u81ec\u8e51',
|
||||
'nin': '\u60a8\u67e0',
|
||||
'ning': '\u72de\u51dd\u5b81\u62e7\u6cde\u4f5e\u84e5\u549b\u752f\u804d',
|
||||
'niu': '\u725b\u626d\u94ae\u7ebd\u72c3\u5ff8\u599e\u86b4',
|
||||
'nong': '\u8113\u6d53\u519c\u4fac',
|
||||
'nu': '\u5974\u52aa\u6012\u5476\u5e11\u5f29\u80ec\u5b65\u9a7d',
|
||||
'nv': '\u5973\u6067\u9495\u8844',
|
||||
'nuan': '\u6696',
|
||||
'nuenue': '\u8650',
|
||||
'nue': '\u759f\u8c11',
|
||||
'nuo': '\u632a\u61e6\u7cef\u8bfa\u50a9\u6426\u558f\u9518',
|
||||
'ou': '\u54e6\u6b27\u9e25\u6bb4\u85d5\u5455\u5076\u6ca4\u6004\u74ef\u8026',
|
||||
'pa': '\u556a\u8db4\u722c\u5e15\u6015\u7436\u8469\u7b62',
|
||||
'pai': '\u62cd\u6392\u724c\u5f98\u6e43\u6d3e\u4ff3\u848e',
|
||||
'pan': '\u6500\u6f58\u76d8\u78d0\u76fc\u7554\u5224\u53db\u723f\u6cee\u88a2\u897b\u87e0\u8e52',
|
||||
'pang': '\u4e53\u5e9e\u65c1\u802a\u80d6\u6ec2\u9004',
|
||||
'pao': '\u629b\u5486\u5228\u70ae\u888d\u8dd1\u6ce1\u530f\u72cd\u5e96\u812c\u75b1',
|
||||
'pei': '\u5478\u80da\u57f9\u88f4\u8d54\u966a\u914d\u4f69\u6c9b\u638a\u8f94\u5e14\u6de0\u65c6\u952b\u9185\u9708',
|
||||
'pen': '\u55b7\u76c6\u6e53',
|
||||
'peng': '\u7830\u62a8\u70f9\u6f8e\u5f6d\u84ec\u68da\u787c\u7bf7\u81a8\u670b\u9e4f\u6367\u78b0\u576f\u580b\u562d\u6026\u87db',
|
||||
'pi': '\u7812\u9739\u6279\u62ab\u5288\u7435\u6bd7\u5564\u813e\u75b2\u76ae\u5339\u75de\u50fb\u5c41\u8b6c\u4e15\u9674\u90b3\u90eb\u572e\u9f19\u64d7\u567c\u5e80\u5ab2\u7eb0\u6787\u7513\u7765\u7f74\u94cd\u75e6\u7656\u758b\u868d\u8c94',
|
||||
'pian': '\u7bc7\u504f\u7247\u9a97\u8c1d\u9a88\u728f\u80fc\u890a\u7fe9\u8e41',
|
||||
'piao': '\u98d8\u6f02\u74e2\u7968\u527d\u560c\u5ad6\u7f25\u6b8d\u779f\u87b5',
|
||||
'pie': '\u6487\u77a5\u4e3f\u82e4\u6c15',
|
||||
'pin': '\u62fc\u9891\u8d2b\u54c1\u8058\u62da\u59d8\u5ad4\u6980\u725d\u98a6',
|
||||
'ping': '\u4e52\u576a\u82f9\u840d\u5e73\u51ed\u74f6\u8bc4\u5c4f\u4fdc\u5a09\u67b0\u9c86',
|
||||
'po': '\u5761\u6cfc\u9887\u5a46\u7834\u9b44\u8feb\u7c95\u53f5\u9131\u6ea5\u73c0\u948b\u94b7\u76a4\u7b38',
|
||||
'pou': '\u5256\u88d2\u8e23',
|
||||
'pu': '\u6251\u94fa\u4ec6\u8386\u8461\u83e9\u84b2\u57d4\u6734\u5703\u666e\u6d66\u8c31\u66dd\u7011\u530d\u5657\u6fee\u749e\u6c06\u9564\u9568\u8e7c',
|
||||
'qi': '\u671f\u6b3a\u6816\u621a\u59bb\u4e03\u51c4\u6f06\u67d2\u6c8f\u5176\u68cb\u5947\u6b67\u7566\u5d0e\u8110\u9f50\u65d7\u7948\u7941\u9a91\u8d77\u5c82\u4e5e\u4f01\u542f\u5951\u780c\u5668\u6c14\u8fc4\u5f03\u6c7d\u6ce3\u8bab\u4e9f\u4e93\u573b\u8291\u840b\u847a\u5601\u5c7a\u5c90\u6c54\u6dc7\u9a90\u7eee\u742a\u7426\u675e\u6864\u69ed\u6b39\u797a\u61a9\u789b\u86f4\u871e\u7da6\u7dae\u8dbf\u8e4a\u9ccd\u9e92',
|
||||
'qia': '\u6390\u6070\u6d3d\u845c',
|
||||
'qian': '\u7275\u6266\u948e\u94c5\u5343\u8fc1\u7b7e\u4edf\u8c26\u4e7e\u9ed4\u94b1\u94b3\u524d\u6f5c\u9063\u6d45\u8c34\u5811\u5d4c\u6b20\u6b49\u4f65\u9621\u828a\u82a1\u8368\u63ae\u5c8d\u60ad\u614a\u9a9e\u6434\u8930\u7f31\u6920\u80b7\u6106\u94a4\u8654\u7b9d',
|
||||
'qiang': '\u67aa\u545b\u8154\u7f8c\u5899\u8537\u5f3a\u62a2\u5af1\u6a2f\u6217\u709d\u9516\u9535\u956a\u8941\u8723\u7f9f\u8deb\u8dc4',
|
||||
'qiao': '\u6a47\u9539\u6572\u6084\u6865\u77a7\u4e54\u4fa8\u5de7\u9798\u64ac\u7fd8\u5ced\u4fcf\u7a8d\u5281\u8bee\u8c2f\u835e\u6100\u6194\u7f32\u6a35\u6bf3\u7857\u8df7\u9792',
|
||||
'qie': '\u5207\u8304\u4e14\u602f\u7a83\u90c4\u553c\u60ec\u59be\u6308\u9532\u7ba7',
|
||||
'qin': '\u94a6\u4fb5\u4eb2\u79e6\u7434\u52e4\u82b9\u64d2\u79bd\u5bdd\u6c81\u82a9\u84c1\u8572\u63ff\u5423\u55ea\u5659\u6eb1\u6a8e\u8793\u887e',
|
||||
'qing': '\u9752\u8f7b\u6c22\u503e\u537f\u6e05\u64ce\u6674\u6c30\u60c5\u9877\u8bf7\u5e86\u5029\u82d8\u570a\u6aa0\u78ec\u873b\u7f44\u7b90\u8b26\u9cad\u9ee5',
|
||||
'qiong': '\u743c\u7a77\u909b\u8315\u7a79\u7b47\u928e',
|
||||
'qiu': '\u79cb\u4e18\u90b1\u7403\u6c42\u56da\u914b\u6cc5\u4fc5\u6c3d\u5def\u827d\u72b0\u6e6b\u9011\u9052\u6978\u8d47\u9e20\u866c\u86af\u8764\u88d8\u7cd7\u9cc5\u9f3d',
|
||||
'qu': '\u8d8b\u533a\u86c6\u66f2\u8eaf\u5c48\u9a71\u6e20\u53d6\u5a36\u9f8b\u8da3\u53bb\u8bce\u52ac\u8556\u8627\u5c96\u8862\u9612\u74a9\u89d1\u6c0d\u795b\u78f2\u766f\u86d0\u883c\u9eb4\u77bf\u9ee2',
|
||||
'quan': '\u5708\u98a7\u6743\u919b\u6cc9\u5168\u75ca\u62f3\u72ac\u5238\u529d\u8be0\u8343\u737e\u609b\u7efb\u8f81\u754e\u94e8\u8737\u7b4c\u9b08',
|
||||
'que': '\u7f3a\u7094\u7638\u5374\u9e4a\u69b7\u786e\u96c0\u9619\u60ab',
|
||||
'qun': '\u88d9\u7fa4\u9021',
|
||||
'ran': '\u7136\u71c3\u5189\u67d3\u82d2\u9aef',
|
||||
'rang': '\u74e4\u58e4\u6518\u56b7\u8ba9\u79b3\u7a70',
|
||||
'rao': '\u9976\u6270\u7ed5\u835b\u5a06\u6861',
|
||||
'ruo': '\u60f9\u82e5\u5f31',
|
||||
're': '\u70ed\u504c',
|
||||
'ren': '\u58ec\u4ec1\u4eba\u5fcd\u97e7\u4efb\u8ba4\u5203\u598a\u7eab\u4ede\u834f\u845a\u996a\u8f6b\u7a14\u887d',
|
||||
'reng': '\u6254\u4ecd',
|
||||
'ri': '\u65e5',
|
||||
'rong': '\u620e\u8338\u84c9\u8363\u878d\u7194\u6eb6\u5bb9\u7ed2\u5197\u5d58\u72e8\u7f1b\u6995\u877e',
|
||||
'rou': '\u63c9\u67d4\u8089\u7cc5\u8e42\u97a3',
|
||||
'ru': '\u8339\u8815\u5112\u5b7a\u5982\u8fb1\u4e73\u6c5d\u5165\u8925\u84d0\u85b7\u5685\u6d33\u6ebd\u6fe1\u94f7\u8966\u98a5',
|
||||
'ruan': '\u8f6f\u962e\u670a',
|
||||
'rui': '\u854a\u745e\u9510\u82ae\u8564\u777f\u868b',
|
||||
'run': '\u95f0\u6da6',
|
||||
'sa': '\u6492\u6d12\u8428\u5345\u4ee8\u6332\u98d2',
|
||||
'sai': '\u816e\u9cc3\u585e\u8d5b\u567b',
|
||||
'san': '\u4e09\u53c1\u4f1e\u6563\u5f61\u9993\u6c35\u6bf5\u7cc1\u9730',
|
||||
'sang': '\u6851\u55d3\u4e27\u6421\u78c9\u98a1',
|
||||
'sao': '\u6414\u9a9a\u626b\u5ac2\u57fd\u81ca\u7619\u9ccb',
|
||||
'se': '\u745f\u8272\u6da9\u556c\u94e9\u94ef\u7a51',
|
||||
'sen': '\u68ee',
|
||||
'seng': '\u50e7',
|
||||
'sha': '\u838e\u7802\u6740\u5239\u6c99\u7eb1\u50bb\u5565\u715e\u810e\u6b43\u75e7\u88df\u970e\u9ca8',
|
||||
'shai': '\u7b5b\u6652\u917e',
|
||||
'shan': '\u73ca\u82eb\u6749\u5c71\u5220\u717d\u886b\u95ea\u9655\u64c5\u8d61\u81b3\u5584\u6c55\u6247\u7f2e\u5261\u8baa\u912f\u57cf\u829f\u6f78\u59d7\u9a9f\u81bb\u9490\u759d\u87ee\u8222\u8dda\u9cdd',
|
||||
'shang': '\u5892\u4f24\u5546\u8d4f\u664c\u4e0a\u5c1a\u88f3\u57a7\u7ef1\u6b87\u71b5\u89de',
|
||||
'shao': '\u68a2\u634e\u7a0d\u70e7\u828d\u52fa\u97f6\u5c11\u54e8\u90b5\u7ecd\u52ad\u82d5\u6f72\u86f8\u7b24\u7b72\u8244',
|
||||
'she': '\u5962\u8d4a\u86c7\u820c\u820d\u8d66\u6444\u5c04\u6151\u6d89\u793e\u8bbe\u538d\u4f58\u731e\u7572\u9e9d',
|
||||
'shen': '\u7837\u7533\u547b\u4f38\u8eab\u6df1\u5a20\u7ec5\u795e\u6c88\u5ba1\u5a76\u751a\u80be\u614e\u6e17\u8bdc\u8c02\u5432\u54c2\u6e16\u6939\u77e7\u8703',
|
||||
'sheng': '\u58f0\u751f\u7525\u7272\u5347\u7ef3\u7701\u76db\u5269\u80dc\u5723\u4e1e\u6e11\u5ab5\u771a\u7b19',
|
||||
'shi': '\u5e08\u5931\u72ee\u65bd\u6e7f\u8bd7\u5c38\u8671\u5341\u77f3\u62fe\u65f6\u4ec0\u98df\u8680\u5b9e\u8bc6\u53f2\u77e2\u4f7f\u5c4e\u9a76\u59cb\u5f0f\u793a\u58eb\u4e16\u67ff\u4e8b\u62ed\u8a93\u901d\u52bf\u662f\u55dc\u566c\u9002\u4ed5\u4f8d\u91ca\u9970\u6c0f\u5e02\u6043\u5ba4\u89c6\u8bd5\u8c25\u57d8\u83b3\u84cd\u5f11\u5511\u9963\u8f7c\u8006\u8d33\u70bb\u793b\u94c8\u94ca\u87ab\u8210\u7b6e\u8c55\u9ca5\u9cba',
|
||||
'shou': '\u6536\u624b\u9996\u5b88\u5bff\u6388\u552e\u53d7\u7626\u517d\u624c\u72e9\u7ef6\u824f',
|
||||
'shu': '\u852c\u67a2\u68b3\u6b8a\u6292\u8f93\u53d4\u8212\u6dd1\u758f\u4e66\u8d4e\u5b70\u719f\u85af\u6691\u66d9\u7f72\u8700\u9ecd\u9f20\u5c5e\u672f\u8ff0\u6811\u675f\u620d\u7ad6\u5885\u5eb6\u6570\u6f31\u6055\u500f\u587e\u83fd\u5fc4\u6cad\u6d91\u6f8d\u59dd\u7ebe\u6bf9\u8167\u6bb3\u956f\u79eb\u9e6c',
|
||||
'shua': '\u5237\u800d\u5530\u6dae',
|
||||
'shuai': '\u6454\u8870\u7529\u5e05\u87c0',
|
||||
'shuan': '\u6813\u62f4\u95e9',
|
||||
'shuang': '\u971c\u53cc\u723d\u5b40',
|
||||
'shui': '\u8c01\u6c34\u7761\u7a0e',
|
||||
'shun': '\u542e\u77ac\u987a\u821c\u6042',
|
||||
'shuo': '\u8bf4\u7855\u6714\u70c1\u84b4\u6420\u55cd\u6fef\u5981\u69ca\u94c4',
|
||||
'si': '\u65af\u6495\u5636\u601d\u79c1\u53f8\u4e1d\u6b7b\u8086\u5bfa\u55e3\u56db\u4f3a\u4f3c\u9972\u5df3\u53ae\u4fdf\u5155\u83e5\u549d\u6c5c\u6cd7\u6f8c\u59d2\u9a77\u7f0c\u7940\u7960\u9536\u9e36\u801c\u86f3\u7b25',
|
||||
'song': '\u677e\u8038\u6002\u9882\u9001\u5b8b\u8bbc\u8bf5\u51c7\u83d8\u5d27\u5d69\u5fea\u609a\u6dde\u7ae6',
|
||||
'sou': '\u641c\u8258\u64de\u55fd\u53df\u55d6\u55fe\u998a\u6eb2\u98d5\u778d\u953c\u878b',
|
||||
'su': '\u82cf\u9165\u4fd7\u7d20\u901f\u7c9f\u50f3\u5851\u6eaf\u5bbf\u8bc9\u8083\u5919\u8c21\u850c\u55c9\u612b\u7c0c\u89eb\u7a23',
|
||||
'suan': '\u9178\u849c\u7b97',
|
||||
'sui': '\u867d\u968b\u968f\u7ee5\u9ad3\u788e\u5c81\u7a57\u9042\u96a7\u795f\u84d1\u51ab\u8c07\u6fc9\u9083\u71e7\u772d\u7762',
|
||||
'sun': '\u5b59\u635f\u7b0b\u836a\u72f2\u98e7\u69ab\u8de3\u96bc',
|
||||
'suo': '\u68ad\u5506\u7f29\u7410\u7d22\u9501\u6240\u5522\u55e6\u5a11\u686b\u7743\u7fa7',
|
||||
'ta': '\u584c\u4ed6\u5b83\u5979\u5854\u736d\u631e\u8e4b\u8e0f\u95fc\u6ebb\u9062\u69bb\u6c93',
|
||||
'tai': '\u80ce\u82d4\u62ac\u53f0\u6cf0\u915e\u592a\u6001\u6c70\u90b0\u85b9\u80bd\u70b1\u949b\u8dc6\u9c90',
|
||||
'tan': '\u574d\u644a\u8d2a\u762b\u6ee9\u575b\u6a80\u75f0\u6f6d\u8c2d\u8c08\u5766\u6bef\u8892\u78b3\u63a2\u53f9\u70ad\u90ef\u8548\u6619\u94bd\u952c\u8983',
|
||||
'tang': '\u6c64\u5858\u642a\u5802\u68e0\u819b\u5510\u7cd6\u50a5\u9967\u6e8f\u746d\u94f4\u9557\u8025\u8797\u87b3\u7fb0\u91a3',
|
||||
'thang': '\u5018\u8eba\u6dcc',
|
||||
'theng': '\u8d9f\u70eb',
|
||||
'tao': '\u638f\u6d9b\u6ed4\u7ee6\u8404\u6843\u9003\u6dd8\u9676\u8ba8\u5957\u6311\u9f17\u5555\u97ec\u9955',
|
||||
'te': '\u7279',
|
||||
'teng': '\u85e4\u817e\u75bc\u8a8a\u6ed5',
|
||||
'ti': '\u68af\u5254\u8e22\u9511\u63d0\u9898\u8e44\u557c\u4f53\u66ff\u568f\u60d5\u6d95\u5243\u5c49\u8351\u608c\u9016\u7ee8\u7f07\u9e48\u88fc\u918d',
|
||||
'tian': '\u5929\u6dfb\u586b\u7530\u751c\u606c\u8214\u8146\u63ad\u5fdd\u9617\u6b84\u754b\u94bf\u86ba',
|
||||
'tiao': '\u6761\u8fe2\u773a\u8df3\u4f7b\u7967\u94eb\u7a95\u9f86\u9ca6',
|
||||
'tie': '\u8d34\u94c1\u5e16\u841c\u992e',
|
||||
'ting': '\u5385\u542c\u70c3\u6c40\u5ef7\u505c\u4ead\u5ead\u633a\u8247\u839b\u8476\u5a77\u6883\u8713\u9706',
|
||||
'tong': '\u901a\u6850\u916e\u77b3\u540c\u94dc\u5f64\u7ae5\u6876\u6345\u7b52\u7edf\u75db\u4f5f\u50ee\u4edd\u833c\u55f5\u6078\u6f7c\u783c',
|
||||
'tou': '\u5077\u6295\u5934\u900f\u4ea0',
|
||||
'tu': '\u51f8\u79c3\u7a81\u56fe\u5f92\u9014\u6d82\u5c60\u571f\u5410\u5154\u580d\u837c\u83df\u948d\u9174',
|
||||
'tuan': '\u6e4d\u56e2\u7583',
|
||||
'tui': '\u63a8\u9893\u817f\u8715\u892a\u9000\u5fd2\u717a',
|
||||
'tun': '\u541e\u5c6f\u81c0\u9968\u66be\u8c5a\u7a80',
|
||||
'tuo': '\u62d6\u6258\u8131\u9e35\u9640\u9a6e\u9a7c\u692d\u59a5\u62d3\u553e\u4e47\u4f57\u5768\u5eb9\u6cb1\u67dd\u7823\u7ba8\u8204\u8dce\u9f0d',
|
||||
'wa': '\u6316\u54c7\u86d9\u6d3c\u5a03\u74e6\u889c\u4f64\u5a32\u817d',
|
||||
'wai': '\u6b6a\u5916',
|
||||
'wan': '\u8c4c\u5f2f\u6e7e\u73a9\u987d\u4e38\u70f7\u5b8c\u7897\u633d\u665a\u7696\u60cb\u5b9b\u5a49\u4e07\u8155\u525c\u8284\u82cb\u83c0\u7ea8\u7efe\u742c\u8118\u7579\u873f\u7ba2',
|
||||
'wang': '\u6c6a\u738b\u4ea1\u6789\u7f51\u5f80\u65fa\u671b\u5fd8\u5984\u7f54\u5c22\u60d8\u8f8b\u9b4d',
|
||||
'wei': '\u5a01\u5dcd\u5fae\u5371\u97e6\u8fdd\u6845\u56f4\u552f\u60df\u4e3a\u6f4d\u7ef4\u82c7\u840e\u59d4\u4f1f\u4f2a\u5c3e\u7eac\u672a\u851a\u5473\u754f\u80c3\u5582\u9b4f\u4f4d\u6e2d\u8c13\u5c09\u6170\u536b\u502d\u504e\u8bff\u9688\u8473\u8587\u5e0f\u5e37\u5d34\u5d6c\u7325\u732c\u95f1\u6ca9\u6d27\u6da0\u9036\u5a13\u73ae\u97ea\u8ece\u709c\u7168\u71a8\u75ff\u8249\u9c94',
|
||||
'wen': '\u761f\u6e29\u868a\u6587\u95fb\u7eb9\u543b\u7a33\u7d0a\u95ee\u520e\u6120\u960c\u6c76\u74ba\u97eb\u6b81\u96ef',
|
||||
'weng': '\u55e1\u7fc1\u74ee\u84ca\u8579',
|
||||
'wo': '\u631d\u8717\u6da1\u7a9d\u6211\u65a1\u5367\u63e1\u6c83\u83b4\u5e44\u6e25\u674c\u809f\u9f8c',
|
||||
'wu': '\u5deb\u545c\u94a8\u4e4c\u6c61\u8bec\u5c4b\u65e0\u829c\u68a7\u543e\u5434\u6bcb\u6b66\u4e94\u6342\u5348\u821e\u4f0d\u4fae\u575e\u620a\u96fe\u6664\u7269\u52ff\u52a1\u609f\u8bef\u5140\u4ef5\u9622\u90ac\u572c\u82b4\u5e91\u6003\u5fe4\u6d6f\u5be4\u8fd5\u59a9\u9a9b\u727e\u7110\u9e49\u9e5c\u8708\u92c8\u9f2f',
|
||||
'xi': '\u6614\u7199\u6790\u897f\u7852\u77fd\u6670\u563b\u5438\u9521\u727a\u7a00\u606f\u5e0c\u6089\u819d\u5915\u60dc\u7184\u70ef\u6eaa\u6c50\u7280\u6a84\u88ad\u5e2d\u4e60\u5ab3\u559c\u94e3\u6d17\u7cfb\u9699\u620f\u7ec6\u50d6\u516e\u96b0\u90d7\u831c\u8478\u84f0\u595a\u550f\u5f99\u9969\u960b\u6d60\u6dc5\u5c63\u5b09\u73ba\u6a28\u66e6\u89cb\u6b37\u71b9\u798a\u79a7\u94b8\u7699\u7a78\u8725\u87cb\u823e\u7fb2\u7c9e\u7fd5\u91af\u9f37',
|
||||
'xia': '\u778e\u867e\u5323\u971e\u8f96\u6687\u5ce1\u4fa0\u72ed\u4e0b\u53a6\u590f\u5413\u6380\u846d\u55c4\u72ce\u9050\u7455\u7856\u7615\u7f45\u9ee0',
|
||||
'xian': '\u9528\u5148\u4ed9\u9c9c\u7ea4\u54b8\u8d24\u8854\u8237\u95f2\u6d8e\u5f26\u5acc\u663e\u9669\u73b0\u732e\u53bf\u817a\u9985\u7fa1\u5baa\u9677\u9650\u7ebf\u51bc\u85d3\u5c98\u7303\u66b9\u5a34\u6c19\u7946\u9e47\u75eb\u86ac\u7b45\u7c7c\u9170\u8df9',
|
||||
'xiang': '\u76f8\u53a2\u9576\u9999\u7bb1\u8944\u6e58\u4e61\u7fd4\u7965\u8be6\u60f3\u54cd\u4eab\u9879\u5df7\u6a61\u50cf\u5411\u8c61\u8297\u8459\u9977\u5ea0\u9aa7\u7f03\u87d3\u9c9e\u98e8',
|
||||
'xiao': '\u8427\u785d\u9704\u524a\u54ee\u56a3\u9500\u6d88\u5bb5\u6dc6\u6653\u5c0f\u5b5d\u6821\u8096\u5578\u7b11\u6548\u54d3\u54bb\u5d24\u6f47\u900d\u9a81\u7ee1\u67ad\u67b5\u7b71\u7bab\u9b48',
|
||||
'xie': '\u6954\u4e9b\u6b47\u874e\u978b\u534f\u631f\u643a\u90aa\u659c\u80c1\u8c10\u5199\u68b0\u5378\u87f9\u61c8\u6cc4\u6cfb\u8c22\u5c51\u5055\u4eb5\u52f0\u71ee\u85a4\u64b7\u5ee8\u7023\u9082\u7ec1\u7f2c\u69ad\u698d\u6b59\u8e9e',
|
||||
'xin': '\u85aa\u82af\u950c\u6b23\u8f9b\u65b0\u5ffb\u5fc3\u4fe1\u8845\u56df\u99a8\u8398\u6b46\u94fd\u946b',
|
||||
'xing': '\u661f\u8165\u7329\u60fa\u5174\u5211\u578b\u5f62\u90a2\u884c\u9192\u5e78\u674f\u6027\u59d3\u9649\u8347\u8365\u64e4\u60bb\u784e',
|
||||
'xiong': '\u5144\u51f6\u80f8\u5308\u6c79\u96c4\u718a\u828e',
|
||||
'xiu': '\u4f11\u4fee\u7f9e\u673d\u55c5\u9508\u79c0\u8896\u7ee3\u83a0\u5cab\u9990\u5ea5\u9e3a\u8c85\u9af9',
|
||||
'xu': '\u589f\u620c\u9700\u865a\u5618\u987b\u5f90\u8bb8\u84c4\u9157\u53d9\u65ed\u5e8f\u755c\u6064\u7d6e\u5a7f\u7eea\u7eed\u8bb4\u8be9\u5729\u84ff\u6035\u6d2b\u6e86\u987c\u6829\u7166\u7809\u76f1\u80e5\u7cc8\u9191',
|
||||
'xuan': '\u8f69\u55a7\u5ba3\u60ac\u65cb\u7384\u9009\u7663\u7729\u7eda\u5107\u8c16\u8431\u63ce\u9994\u6ceb\u6d35\u6e32\u6f29\u7487\u6966\u6684\u70ab\u714a\u78b9\u94c9\u955f\u75c3',
|
||||
'xue': '\u9774\u859b\u5b66\u7a74\u96ea\u8840\u5671\u6cf6\u9cd5',
|
||||
'xun': '\u52cb\u718f\u5faa\u65ec\u8be2\u5bfb\u9a6f\u5de1\u6b89\u6c5b\u8bad\u8baf\u900a\u8fc5\u5dfd\u57d9\u8340\u85b0\u5ccb\u5f87\u6d54\u66db\u7aa8\u91ba\u9c9f',
|
||||
'ya': '\u538b\u62bc\u9e26\u9e2d\u5440\u4e2b\u82bd\u7259\u869c\u5d16\u8859\u6daf\u96c5\u54d1\u4e9a\u8bb6\u4f22\u63e0\u5416\u5c88\u8fd3\u5a05\u740a\u6860\u6c29\u7811\u775a\u75d6',
|
||||
'yan': '\u7109\u54bd\u9609\u70df\u6df9\u76d0\u4e25\u7814\u8712\u5ca9\u5ef6\u8a00\u989c\u960e\u708e\u6cbf\u5944\u63a9\u773c\u884d\u6f14\u8273\u5830\u71d5\u538c\u781a\u96c1\u5501\u5f66\u7130\u5bb4\u8c1a\u9a8c\u53a3\u9765\u8d5d\u4fe8\u5043\u5156\u8ba0\u8c33\u90fe\u9122\u82ab\u83f8\u5d26\u6079\u95eb\u960f\u6d07\u6e6e\u6edf\u598d\u5ae3\u7430\u664f\u80ed\u814c\u7131\u7f68\u7b75\u917d\u9b47\u990d\u9f39',
|
||||
'yang': '\u6b83\u592e\u9e2f\u79e7\u6768\u626c\u4f6f\u75a1\u7f8a\u6d0b\u9633\u6c27\u4ef0\u75d2\u517b\u6837\u6f3e\u5f89\u600f\u6cf1\u7080\u70ca\u6059\u86d8\u9785',
|
||||
'yao': '\u9080\u8170\u5996\u7476\u6447\u5c27\u9065\u7a91\u8c23\u59da\u54ac\u8200\u836f\u8981\u8000\u592d\u723b\u5406\u5d3e\u5fad\u7039\u5e7a\u73e7\u6773\u66dc\u80b4\u9e5e\u7a88\u7e47\u9cd0',
|
||||
'ye': '\u6930\u564e\u8036\u7237\u91ce\u51b6\u4e5f\u9875\u6396\u4e1a\u53f6\u66f3\u814b\u591c\u6db2\u8c12\u90ba\u63f6\u9980\u6654\u70e8\u94d8',
|
||||
'yi': '\u4e00\u58f9\u533b\u63d6\u94f1\u4f9d\u4f0a\u8863\u9890\u5937\u9057\u79fb\u4eea\u80f0\u7591\u6c82\u5b9c\u59e8\u5f5d\u6905\u8681\u501a\u5df2\u4e59\u77e3\u4ee5\u827a\u6291\u6613\u9091\u5c79\u4ebf\u5f79\u81c6\u9038\u8084\u75ab\u4ea6\u88d4\u610f\u6bc5\u5fc6\u4e49\u76ca\u6ea2\u8be3\u8bae\u8c0a\u8bd1\u5f02\u7ffc\u7fcc\u7ece\u5208\u5293\u4f7e\u8bd2\u572a\u572f\u57f8\u61ff\u82e1\u858f\u5f08\u5955\u6339\u5f0b\u5453\u54a6\u54bf\u566b\u5cc4\u5db7\u7317\u9974\u603f\u6021\u6092\u6f2a\u8fe4\u9a7f\u7f22\u6baa\u8d3b\u65d6\u71a0\u9487\u9552\u9571\u75cd\u7617\u7654\u7fca\u8864\u8734\u8223\u7fbf\u7ff3\u914f\u9edf',
|
||||
'yin': '\u8335\u836b\u56e0\u6bb7\u97f3\u9634\u59fb\u541f\u94f6\u6deb\u5bc5\u996e\u5c39\u5f15\u9690\u5370\u80e4\u911e\u5819\u831a\u5591\u72fa\u5924\u6c24\u94df\u763e\u8693\u972a\u9f88',
|
||||
'ying': '\u82f1\u6a31\u5a74\u9e70\u5e94\u7f28\u83b9\u8424\u8425\u8367\u8747\u8fce\u8d62\u76c8\u5f71\u9896\u786c\u6620\u5b34\u90e2\u8314\u83ba\u8426\u6484\u5624\u81ba\u6ee2\u6f46\u701b\u745b\u748e\u6979\u9e66\u763f\u988d\u7f42',
|
||||
'yo': '\u54df\u5537',
|
||||
'yong': '\u62e5\u4f63\u81c3\u75c8\u5eb8\u96cd\u8e0a\u86f9\u548f\u6cf3\u6d8c\u6c38\u607f\u52c7\u7528\u4fd1\u58c5\u5889\u6175\u9095\u955b\u752c\u9cd9\u9954',
|
||||
'you': '\u5e7d\u4f18\u60a0\u5fe7\u5c24\u7531\u90ae\u94c0\u72b9\u6cb9\u6e38\u9149\u6709\u53cb\u53f3\u4f51\u91c9\u8bf1\u53c8\u5e7c\u5363\u6538\u4f91\u83b8\u5466\u56ff\u5ba5\u67da\u7337\u7256\u94d5\u75a3\u8763\u9c7f\u9edd\u9f2c',
|
||||
'yu': '\u8fc2\u6de4\u4e8e\u76c2\u6986\u865e\u611a\u8206\u4f59\u4fde\u903e\u9c7c\u6109\u6e1d\u6e14\u9685\u4e88\u5a31\u96e8\u4e0e\u5c7f\u79b9\u5b87\u8bed\u7fbd\u7389\u57df\u828b\u90c1\u5401\u9047\u55bb\u5cea\u5fa1\u6108\u6b32\u72f1\u80b2\u8a89\u6d74\u5bd3\u88d5\u9884\u8c6b\u9a6d\u79ba\u6bd3\u4f1b\u4fe3\u8c00\u8c15\u8438\u84e3\u63c4\u5581\u5704\u5709\u5d5b\u72f3\u996b\u5ebe\u9608\u59aa\u59a4\u7ea1\u745c\u6631\u89ce\u8174\u6b24\u65bc\u715c\u71e0\u807f\u94b0\u9e46\u7610\u7600\u7ab3\u8753\u7afd\u8201\u96e9\u9f89',
|
||||
'yuan': '\u9e33\u6e0a\u51a4\u5143\u57a3\u8881\u539f\u63f4\u8f95\u56ed\u5458\u5706\u733f\u6e90\u7f18\u8fdc\u82d1\u613f\u6028\u9662\u586c\u6c85\u5a9b\u7457\u6a7c\u7230\u7722\u9e22\u8788\u9f0b',
|
||||
'yue': '\u66f0\u7ea6\u8d8a\u8dc3\u94a5\u5cb3\u7ca4\u6708\u60a6\u9605\u9fa0\u6a3e\u5216\u94ba',
|
||||
'yun': '\u8018\u4e91\u90e7\u5300\u9668\u5141\u8fd0\u8574\u915d\u6655\u97f5\u5b55\u90d3\u82b8\u72c1\u607d\u7ead\u6b92\u6600\u6c32',
|
||||
'za': '\u531d\u7838\u6742\u62f6\u5482',
|
||||
'zai': '\u683d\u54c9\u707e\u5bb0\u8f7d\u518d\u5728\u54b1\u5d3d\u753e',
|
||||
'zan': '\u6512\u6682\u8d5e\u74d2\u661d\u7c2a\u7ccc\u8db1\u933e',
|
||||
'zang': '\u8d43\u810f\u846c\u5958\u6215\u81e7',
|
||||
'zao': '\u906d\u7cdf\u51ff\u85fb\u67a3\u65e9\u6fa1\u86a4\u8e81\u566a\u9020\u7682\u7076\u71e5\u5523\u7f2b',
|
||||
'ze': '\u8d23\u62e9\u5219\u6cfd\u4ec4\u8d5c\u5567\u8fee\u6603\u7b2e\u7ba6\u8234',
|
||||
'zei': '\u8d3c',
|
||||
'zen': '\u600e\u8c2e',
|
||||
'zeng': '\u589e\u618e\u66fe\u8d60\u7f2f\u7511\u7f7e\u9503',
|
||||
'zha': '\u624e\u55b3\u6e23\u672d\u8f67\u94e1\u95f8\u7728\u6805\u69a8\u548b\u4e4d\u70b8\u8bc8\u63f8\u5412\u54a4\u54f3\u600d\u781f\u75c4\u86b1\u9f44',
|
||||
'zhai': '\u6458\u658b\u5b85\u7a84\u503a\u5be8\u7826',
|
||||
'zhan': '\u77bb\u6be1\u8a79\u7c98\u6cbe\u76cf\u65a9\u8f97\u5d2d\u5c55\u8638\u6808\u5360\u6218\u7ad9\u6e5b\u7efd\u8c35\u640c\u65c3',
|
||||
'zhang': '\u6a1f\u7ae0\u5f70\u6f33\u5f20\u638c\u6da8\u6756\u4e08\u5e10\u8d26\u4ed7\u80c0\u7634\u969c\u4ec9\u9123\u5e5b\u5d82\u7350\u5adc\u748b\u87d1',
|
||||
'zhao': '\u62db\u662d\u627e\u6cbc\u8d75\u7167\u7f69\u5146\u8087\u53ec\u722a\u8bcf\u68f9\u948a\u7b0a',
|
||||
'zhe': '\u906e\u6298\u54f2\u86f0\u8f99\u8005\u9517\u8517\u8fd9\u6d59\u8c2a\u966c\u67d8\u8f84\u78d4\u9e67\u891a\u8707\u8d6d',
|
||||
'zhen': '\u73cd\u659f\u771f\u7504\u7827\u81fb\u8d1e\u9488\u4fa6\u6795\u75b9\u8bca\u9707\u632f\u9547\u9635\u7f1c\u6862\u699b\u8f78\u8d48\u80d7\u6715\u796f\u755b\u9e29',
|
||||
'zheng': '\u84b8\u6323\u7741\u5f81\u72f0\u4e89\u6014\u6574\u62ef\u6b63\u653f\u5e27\u75c7\u90d1\u8bc1\u8be4\u5ce5\u94b2\u94ee\u7b5d',
|
||||
'zhi': '\u829d\u679d\u652f\u5431\u8718\u77e5\u80a2\u8102\u6c41\u4e4b\u7ec7\u804c\u76f4\u690d\u6b96\u6267\u503c\u4f84\u5740\u6307\u6b62\u8dbe\u53ea\u65e8\u7eb8\u5fd7\u631a\u63b7\u81f3\u81f4\u7f6e\u5e1c\u5cd9\u5236\u667a\u79e9\u7a1a\u8d28\u7099\u75d4\u6ede\u6cbb\u7a92\u536e\u965f\u90c5\u57f4\u82b7\u646d\u5e19\u5fee\u5f58\u54ab\u9a98\u6809\u67b3\u6800\u684e\u8f75\u8f7e\u6534\u8d3d\u81a3\u7949\u7957\u9ef9\u96c9\u9e37\u75e3\u86ed\u7d77\u916f\u8dd6\u8e2c\u8e2f\u8c78\u89ef',
|
||||
'zhong': '\u4e2d\u76c5\u5fe0\u949f\u8877\u7ec8\u79cd\u80bf\u91cd\u4ef2\u4f17\u51a2\u953a\u87bd\u8202\u822f\u8e35',
|
||||
'zhou': '\u821f\u5468\u5dde\u6d32\u8bcc\u7ca5\u8f74\u8098\u5e1a\u5492\u76b1\u5b99\u663c\u9aa4\u5544\u7740\u501c\u8bf9\u836e\u9b3b\u7ea3\u80c4\u78a1\u7c40\u8233\u914e\u9cb7',
|
||||
'zhu': '\u73e0\u682a\u86db\u6731\u732a\u8bf8\u8bdb\u9010\u7af9\u70db\u716e\u62c4\u77a9\u5631\u4e3b\u8457\u67f1\u52a9\u86c0\u8d2e\u94f8\u7b51\u4f4f\u6ce8\u795d\u9a7b\u4f2b\u4f8f\u90be\u82ce\u8331\u6d19\u6e1a\u6f74\u9a7a\u677c\u69e0\u6a65\u70b7\u94e2\u75b0\u7603\u86b0\u7afa\u7bb8\u7fe5\u8e85\u9e88',
|
||||
'zhua': '\u6293',
|
||||
'zhuai': '\u62fd',
|
||||
'zhuan': '\u4e13\u7816\u8f6c\u64b0\u8d5a\u7bc6\u629f\u556d\u989b',
|
||||
'zhuang': '\u6869\u5e84\u88c5\u5986\u649e\u58ee\u72b6\u4e2c',
|
||||
'zhui': '\u690e\u9525\u8ffd\u8d58\u5760\u7f00\u8411\u9a93\u7f12',
|
||||
'zhun': '\u8c06\u51c6',
|
||||
'zhuo': '\u6349\u62d9\u5353\u684c\u7422\u8301\u914c\u707c\u6d4a\u502c\u8bfc\u5ef4\u855e\u64e2\u555c\u6d5e\u6dbf\u6753\u712f\u799a\u65ab',
|
||||
'zi': '\u5179\u54a8\u8d44\u59ff\u6ecb\u6dc4\u5b5c\u7d2b\u4ed4\u7c7d\u6ed3\u5b50\u81ea\u6e0d\u5b57\u8c18\u5d6b\u59ca\u5b73\u7f01\u6893\u8f8e\u8d40\u6063\u7726\u9531\u79ed\u8014\u7b2b\u7ca2\u89dc\u8a3e\u9cbb\u9aed',
|
||||
'zong': '\u9b03\u68d5\u8e2a\u5b97\u7efc\u603b\u7eb5\u8159\u7cbd',
|
||||
'zou': '\u90b9\u8d70\u594f\u63cd\u9139\u9cb0',
|
||||
'zu': '\u79df\u8db3\u5352\u65cf\u7956\u8bc5\u963b\u7ec4\u4fce\u83f9\u5550\u5f82\u9a75\u8e74',
|
||||
'zuan': '\u94bb\u7e82\u6525\u7f35',
|
||||
'zui': '\u5634\u9189\u6700\u7f6a',
|
||||
'zun': '\u5c0a\u9075\u6499\u6a3d\u9cdf',
|
||||
'zuo': '\u6628\u5de6\u4f50\u67de\u505a\u4f5c\u5750\u5ea7\u961d\u963c\u80d9\u795a\u9162',
|
||||
'cou': '\u85ae\u6971\u8f8f\u8160',
|
||||
'nang': '\u652e\u54dd\u56d4\u9995\u66e9',
|
||||
'o': '\u5594',
|
||||
'dia': '\u55f2',
|
||||
'chuai': '\u562c\u81aa\u8e39',
|
||||
'cen': '\u5c91\u6d94',
|
||||
'diu': '\u94e5',
|
||||
'nou': '\u8028',
|
||||
'fou': '\u7f36',
|
||||
'bia': '\u9adf'
|
||||
};
|
||||
export default {
|
||||
chineseToPinYin: function (l1) {
|
||||
var l2 = l1.length
|
||||
var I1 = ''
|
||||
var reg = new RegExp('[a-zA-Z0-9]')
|
||||
for (var i = 0; i < l2; i++) {
|
||||
var val = l1.substr(i, 1)
|
||||
var name = this.arraySearch(val, pinyin)
|
||||
if (reg.test(val)) {
|
||||
I1 += val
|
||||
} else if (name !== false) {
|
||||
I1 += name
|
||||
}
|
||||
}
|
||||
I1 = I1.replace(/ /g, '-')
|
||||
while (I1.indexOf('--') > 0) {
|
||||
I1 = I1.replace('--', '-')
|
||||
}
|
||||
return I1
|
||||
},
|
||||
arraySearch: function (l1, l2) {
|
||||
for (var name in pinyin) {
|
||||
if (pinyin[name].indexOf(l1) !== -1) {
|
||||
return this.ucfirst(name)
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
ucfirst: function (l1) {
|
||||
if (l1.length > 0) {
|
||||
var first = l1.substr(0, 1).toUpperCase()
|
||||
var spare = l1.substr(1, l1.length)
|
||||
return first + spare
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<wd-popup v-model="show" position="right" @close="handleClose">
|
||||
<view class="content">
|
||||
<wd-text v-if="title" :text="title"></wd-text>
|
||||
<wd-cell-group border>
|
||||
<wd-radio-group v-model="checked">
|
||||
<template v-for="(item, index) in options">
|
||||
<wd-cell :title="item.title" clickable @click="handleSelected(item)">
|
||||
<wd-radio :value="item.key"></wd-radio>
|
||||
</wd-cell>
|
||||
</template>
|
||||
</wd-radio-group>
|
||||
</wd-cell-group>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { hasRoute, cache } from '@/common/uitls'
|
||||
import { ref } from 'vue'
|
||||
const eimt = defineEmits(['change', 'close'])
|
||||
const show = ref(true)
|
||||
const props = defineProps(['title', 'data', 'options', 'checked'])
|
||||
const checked = ref(props.checked)
|
||||
const handleClose = () => {
|
||||
show.value = false
|
||||
setTimeout(() => {
|
||||
eimt('close')
|
||||
}, 300)
|
||||
}
|
||||
const handleSelected = (item) => {
|
||||
checked.value = item.key
|
||||
eimt('change', { option: item, data: props.data })
|
||||
handleClose()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
max-width: 200px;
|
||||
padding: 10px;
|
||||
.wd-text.is-default {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
.wd-cell {
|
||||
padding-left: 0;
|
||||
--wot-cell-label-color: #444;
|
||||
--wot-cell-label-fs: 14px;
|
||||
&.red {
|
||||
color: red;
|
||||
--wot-cell-label-color: red;
|
||||
}
|
||||
}
|
||||
.wd-cell-group {
|
||||
:deep(.wd-cell__wrapper) {
|
||||
align-items: center;
|
||||
.wd-cell__right {
|
||||
flex: none;
|
||||
width: 24px;
|
||||
}
|
||||
.wd-radio {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
256
src/pages-message/contacts/contacts.vue
Normal file
256
src/pages-message/contacts/contacts.vue
Normal file
@ -0,0 +1,256 @@
|
||||
<route lang="json5" type="page">
|
||||
{
|
||||
layout: 'default',
|
||||
style: {
|
||||
navigationStyle: 'custom',
|
||||
navigationBarTitleText: '联系人',
|
||||
},
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<PageLayout navTitle="联系人" backRouteName="message" routeMethod="pushTab">
|
||||
<view class="wrap">
|
||||
<z-paging
|
||||
ref="paging"
|
||||
:fixed="false"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
:default-page-size="100"
|
||||
>
|
||||
<template #top>
|
||||
<wd-search
|
||||
hide-cancel
|
||||
placeholder="我要去哪里?"
|
||||
v-model="keyword"
|
||||
@search="handleSearch"
|
||||
@clear="handleClear"
|
||||
/>
|
||||
</template>
|
||||
<view class="wraper">
|
||||
<wd-index-bar sticky v-if="dataSource.length">
|
||||
<view v-for="item in dataSource" :key="item.index">
|
||||
<wd-index-anchor :index="item.index" />
|
||||
<wd-cell
|
||||
border
|
||||
clickable
|
||||
v-for="inItem in item.data"
|
||||
:key="item.username"
|
||||
@click="handleClick(item.index, inItem)"
|
||||
>
|
||||
<template #icon>
|
||||
<wd-img
|
||||
customClass="avatar"
|
||||
:width="50"
|
||||
:height="50"
|
||||
:src="getFileAccessHttpUrl(inItem.avatar) || defaultAvatar"
|
||||
></wd-img>
|
||||
</template>
|
||||
<template #title>
|
||||
<view class="content text-gray-4">
|
||||
<text>{{ inItem.realname }}</text>
|
||||
<text>{{ inItem.orgCodeTxt ?? '暂无' }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</wd-cell>
|
||||
</view>
|
||||
</wd-index-bar>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
<template #navRight>
|
||||
<view
|
||||
class="cuIcon-filter font-size-20px color-white"
|
||||
@click="() => (conditionFilter.show = true)"
|
||||
></view>
|
||||
</template>
|
||||
<rightConditionFilter
|
||||
v-if="conditionFilter.show"
|
||||
v-bind="conditionFilter"
|
||||
@close="() => (conditionFilter.show = false)"
|
||||
@change="handleChange"
|
||||
></rightConditionFilter>
|
||||
</PageLayout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onLaunch, onShow, onHide, onLoad, onReady } from '@dcloudio/uni-app'
|
||||
import { nextTick, onMounted, ref } from 'vue'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { http } from '@/utils/http'
|
||||
import { useParamsStore } from '@/store/page-params'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { useRouter } from '@/plugin/uni-mini-router'
|
||||
import { cache, getFileAccessHttpUrl, hasRoute } from '@/common/uitls'
|
||||
import vPinyin from '../common/vue-py'
|
||||
import rightConditionFilter from '@/components/RightConditionFilter/RightConditionFilter.vue'
|
||||
import { TENANT_LIST } from '@/common/constants'
|
||||
import defaultAvatar from '@/static/default-avatar.png';
|
||||
|
||||
const toast = useToast()
|
||||
const userStore = useUserStore()
|
||||
const paramsStore = useParamsStore()
|
||||
const paging = ref(null)
|
||||
const router = useRouter()
|
||||
const dataList = ref([])
|
||||
// 接口拿到的数据处理之后的
|
||||
const originData = ref([])
|
||||
const keyword = ref('')
|
||||
const dataSource = ref([])
|
||||
const conditionFilter = reactive({ show: false, checked: '', options: [] })
|
||||
|
||||
const queryList = (pageNo, pageSize) => {
|
||||
const pararms = { pageNo, pageSize, tenantId: conditionFilter.checked }
|
||||
if (conditionFilter.checked === '') delete pararms.tenantId
|
||||
http
|
||||
.get('/sys/user/appQueryUser', pararms)
|
||||
.then((res: any) => {
|
||||
if (res.success && res.result.length) {
|
||||
paging.value.complete(res.result)
|
||||
} else {
|
||||
paging.value.complete(false)
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
paging.value.complete(false)
|
||||
})
|
||||
}
|
||||
// 监听接口数据
|
||||
watch(dataList, () => {
|
||||
let result = handleResult(dataList.value)
|
||||
result = transformData(result)
|
||||
result.sort((a, b) => a.index.localeCompare(b.index))
|
||||
originData.value = [...result]
|
||||
dataSource.value = result
|
||||
console.log('dataSource:::', dataSource.value)
|
||||
})
|
||||
|
||||
// 搜索
|
||||
function handleSearch() {
|
||||
dataSource.value = []
|
||||
nextTick(() => {
|
||||
if (keyword.value) {
|
||||
dataSource.value = originData.value.filter((item) => {
|
||||
return item.data.some((inItem) => {
|
||||
return inItem.realname.indexOf(keyword.value) != -1
|
||||
})
|
||||
})
|
||||
} else {
|
||||
dataSource.value = originData.value
|
||||
}
|
||||
})
|
||||
}
|
||||
// 清除搜索条件
|
||||
function handleClear() {
|
||||
keyword.value = ''
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
const handleClick = (index: string, data: any) => {
|
||||
paramsStore.setPageParams('personPage', { backRouteName: 'contacts', data })
|
||||
router.push({ name: 'personPage' })
|
||||
}
|
||||
const handleChange = ({ option }) => {
|
||||
conditionFilter.checked = option.key
|
||||
paging.value.reload()
|
||||
}
|
||||
const transformData = (data) => {
|
||||
const grouped = data.reduce((acc, item) => {
|
||||
const key = item.szm
|
||||
if (!acc[key]) {
|
||||
acc[key] = []
|
||||
}
|
||||
acc[key].push(item)
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
return Object.entries(grouped)
|
||||
.map(([index, data]) => ({ index, data }))
|
||||
.sort((a, b) => b.index.localeCompare(a.index))
|
||||
}
|
||||
const handleResult = (arr) => {
|
||||
let newArr = []
|
||||
arr.forEach((item) => {
|
||||
let { id, realname, avatar, username, phone, email, post, orgCodeTxt } = item
|
||||
//聊天通讯录把自己过滤掉
|
||||
if (username !== userStore.userInfo.username) {
|
||||
let pinYin = realname
|
||||
if (realname) {
|
||||
//TODO 判断汉字的位置
|
||||
if (/.*[\u4e00-\u9fa5]+.*$/.test(realname)) {
|
||||
pinYin = vPinyin.chineseToPinYin(realname)
|
||||
}
|
||||
}
|
||||
if (avatar) {
|
||||
avatar = getFileAccessHttpUrl(avatar)
|
||||
}
|
||||
let szm = pinYin.substr(0, 1)
|
||||
var numReg = /^[0-9]*$/
|
||||
var numRe = new RegExp(numReg)
|
||||
szm = !numRe.test(szm) ? szm.toUpperCase() : '#'
|
||||
newArr.push({
|
||||
id,
|
||||
realname,
|
||||
avatar,
|
||||
username,
|
||||
phone,
|
||||
email,
|
||||
post,
|
||||
orgCodeTxt,
|
||||
szm: szm,
|
||||
})
|
||||
}
|
||||
})
|
||||
return newArr
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
const tenantList = cache(TENANT_LIST)
|
||||
const result = tenantList?.map((item) => {
|
||||
return { key: item.id, title: item.name }
|
||||
})
|
||||
conditionFilter.options = [{ key: '', title: '全部' }, ...result]
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wrap {
|
||||
height: 100%;
|
||||
}
|
||||
.z-paging-content {
|
||||
// :deep(.zp-paging-container) {
|
||||
// flex: none;
|
||||
// height: calc(100% - 42px);
|
||||
// .zp-paging-container-content {
|
||||
// height: 100%;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
:deep(.avatar) {
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin-right: 10px;
|
||||
background-color: #eee;
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
uni-text {
|
||||
&:first-child {
|
||||
font-size: 16px;
|
||||
color: #8799a3;
|
||||
}
|
||||
&:last-child {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.wraper {
|
||||
// height: 100%;
|
||||
height: calc(100vh - var(--window-top) - constant(safe-area-inset-bottom) - 140px);
|
||||
height: calc(100vh - var(--window-top) - env(safe-area-inset-bottom) - 140px);
|
||||
:deep(.wd-cell__right) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
126
src/pages-message/personPage/personPage.vue
Normal file
126
src/pages-message/personPage/personPage.vue
Normal file
@ -0,0 +1,126 @@
|
||||
<route lang="json5" type="page">
|
||||
{
|
||||
layout: 'default',
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<PageLayout
|
||||
:backRouteName="backRouteName"
|
||||
navLeftText=""
|
||||
:navBgTransparent="true"
|
||||
:navFixed="true"
|
||||
>
|
||||
<view class="wrap">
|
||||
<view class="topArea"></view>
|
||||
<view class="middleArea bg-white">
|
||||
<wd-img
|
||||
custom-class="avatar"
|
||||
width="75px"
|
||||
height="75px"
|
||||
radius="50%"
|
||||
:src="data.avatar"
|
||||
></wd-img>
|
||||
<wd-text custom-class="realname center" :text="data.realname"></wd-text>
|
||||
<wd-button custom-class="" @click="handleGo">发消息</wd-button>
|
||||
</view>
|
||||
<view class="bottomArea bg-white">
|
||||
<view class="list">
|
||||
<view class="iconBox">
|
||||
<view class="cuIcon-mobile text-gray-4"></view>
|
||||
</view>
|
||||
<view class="content">
|
||||
<view class="label text-gray-4">手机</view>
|
||||
<view class="value text-blue-5">{{ data.phone || '未填写' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list">
|
||||
<view class="iconBox">
|
||||
<view class="cuIcon-mail text-gray-4"></view>
|
||||
</view>
|
||||
<view class="content">
|
||||
<view class="label text-gray-4">手机</view>
|
||||
<view class="value text-blue-5">{{ data.email || '未填写' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</PageLayout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { useParamsStore } from '@/store/page-params'
|
||||
import { useRouter } from '@/plugin/uni-mini-router'
|
||||
//
|
||||
const userStore = useUserStore()
|
||||
const paramsStore = useParamsStore()
|
||||
const router = useRouter()
|
||||
const params = paramsStore.getPageParams('personPage')
|
||||
const backRouteName = ref(params.backRouteName) ?? {}
|
||||
let data = params.data ?? {}
|
||||
|
||||
|
||||
const handleGo = () => {
|
||||
var parmas = {
|
||||
fromAvatar: data.avatar,
|
||||
fromUserName: data.realname || data.username,
|
||||
msgFrom: userStore.userInfo.userid,
|
||||
msgTo: data.id,
|
||||
type: 'friend',
|
||||
}
|
||||
paramsStore.setPageParams('chat', { back: 'personPage', data: parmas })
|
||||
router.push({ name: 'chat' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
//
|
||||
.topArea {
|
||||
background: linear-gradient(45deg, #0081ff, #1cbbb4);
|
||||
min-height: 170px;
|
||||
}
|
||||
.middleArea {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 30px;
|
||||
.avatar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.realname {
|
||||
padding-top: 50px;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
.bottomArea {
|
||||
.list {
|
||||
border-top: 1px solid #f1f1f1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
.iconBox {
|
||||
font-size: 28px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.label {
|
||||
font-size: 15px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
.value {
|
||||
color: #3665cb;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
117
src/pages-message/tenant/components/department.vue
Normal file
117
src/pages-message/tenant/components/department.vue
Normal file
@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<view class="wrap">
|
||||
<view class="content">
|
||||
<scroll-view class="scrollArea" scroll-y>
|
||||
<template v-if="dataSource.length">
|
||||
<template v-for="(item, index) in dataSource">
|
||||
<wd-cell border clickable @click="handleClick(item)">
|
||||
<template #icon>
|
||||
<wd-img
|
||||
customClass="mr2"
|
||||
:radius="getRadius(item)"
|
||||
:width="30"
|
||||
:height="30"
|
||||
:src="getImg(item)"
|
||||
></wd-img>
|
||||
</template>
|
||||
<template #title>
|
||||
<view class="content text-gray-4">
|
||||
<text>{{ getName(item) }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</wd-cell>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<wd-status-tip image="content" tip="暂无内容" />
|
||||
</template>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
<wd-toast />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onLaunch, onShow, onHide, onLoad, onReady } from '@dcloudio/uni-app'
|
||||
import { nextTick, onMounted, ref } from 'vue'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { http } from '@/utils/http'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { useRouter } from '@/plugin/uni-mini-router'
|
||||
import { useParamsStore } from '@/store/page-params'
|
||||
import defaultAvatar from '@/static/default-avatar.png'
|
||||
import folderImg from '@/static/folder.png'
|
||||
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const toast = useToast()
|
||||
const props = defineProps(['tenantId'])
|
||||
const dataSource: any = ref([])
|
||||
const paramsStore = useParamsStore()
|
||||
const api = {
|
||||
depTree: '/sys/sysDepart/queryBookDepTreeSync',
|
||||
list: '/sys/user/list',
|
||||
}
|
||||
const getImg = (item) => {
|
||||
if (item.parentId != null) {
|
||||
return folderImg
|
||||
} else {
|
||||
return item.avatar
|
||||
}
|
||||
}
|
||||
const getName = (item) => {
|
||||
if (item.parentId != null) {
|
||||
return item.departName
|
||||
} else {
|
||||
return item.realname
|
||||
}
|
||||
}
|
||||
const getRadius = (item) => {
|
||||
return item.parentId != null ? null : '50%'
|
||||
}
|
||||
|
||||
const handleClick = (item) => {
|
||||
if (item.parentId != null) {
|
||||
query({ id: item.id })
|
||||
} else {
|
||||
paramsStore.setPageParams('personPage', { data: item })
|
||||
router.push({ name: 'personPage' })
|
||||
}
|
||||
}
|
||||
const query = (params: any = {}) => {
|
||||
const pararms = { pid: params.id ?? '', departId: params.id, tenantId: props.tenantId }
|
||||
Promise.all([
|
||||
http.get(api.depTree, pararms),
|
||||
pararms.pid
|
||||
? http.get(api.list, pararms)
|
||||
: Promise.resolve({ success: true, result: { records: [] } }),
|
||||
])
|
||||
.then((res: any) => {
|
||||
if (res[0].success == true && res[1].success == true) {
|
||||
const result = res[0]?.result ?? []
|
||||
const records = res[1]?.result?.records ?? []
|
||||
const data = [...result, ...records]
|
||||
if (params.id) {
|
||||
// 证明是点击
|
||||
if (data.length) {
|
||||
dataSource.value = data
|
||||
} else {
|
||||
toast.warning('下一级无数据~')
|
||||
}
|
||||
} else {
|
||||
dataSource.value = data
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((res) => {})
|
||||
}
|
||||
query()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.wd-cell) {
|
||||
.wd-cell__right {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
234
src/pages-message/tenant/components/workmate.vue
Normal file
234
src/pages-message/tenant/components/workmate.vue
Normal file
@ -0,0 +1,234 @@
|
||||
<template>
|
||||
<view class="wrap">
|
||||
<!-- prettier-ignore -->
|
||||
<z-paging ref="paging" :fixed="false" v-model="dataList" @query="queryList" :default-page-size="100">
|
||||
<template #top>
|
||||
<wd-search
|
||||
hide-cancel
|
||||
placeholder="我要去哪里?"
|
||||
v-model="keyword"
|
||||
@search="handleSearch"
|
||||
@clear="handleClear"
|
||||
/>
|
||||
</template>
|
||||
<view class="wraper">
|
||||
<wd-index-bar sticky v-if="dataSource.length">
|
||||
<view v-for="item in dataSource" :key="item.index">
|
||||
<wd-index-anchor :index="item.index" />
|
||||
<wd-cell
|
||||
border
|
||||
clickable
|
||||
v-for="inItem in item.data"
|
||||
:key="item.username"
|
||||
@click="handleClick(item.index, inItem)"
|
||||
>
|
||||
<template #icon>
|
||||
<wd-img customClass="avatar" :width="50" :height="50" :src="inItem.avatar || defaultAvatar"></wd-img>
|
||||
</template>
|
||||
<template #title>
|
||||
<view class="content text-gray-4">
|
||||
<text>{{ inItem.realname }}</text>
|
||||
<text>{{ inItem.orgCodeTxt ?? '暂无' }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</wd-cell>
|
||||
</view>
|
||||
</wd-index-bar>
|
||||
<template v-else>
|
||||
<wd-status-tip image="content" tip="暂无内容" />
|
||||
</template>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onLaunch, onShow, onHide, onLoad, onReady } from '@dcloudio/uni-app'
|
||||
import { nextTick, onMounted, ref } from 'vue'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { http } from '@/utils/http'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { useRouter } from '@/plugin/uni-mini-router'
|
||||
import { cache, getFileAccessHttpUrl, hasRoute } from '@/common/uitls'
|
||||
import vPinyin from '../../common/vue-py'
|
||||
import { TENANT_LIST } from '@/common/constants'
|
||||
import { useParamsStore } from '@/store/page-params'
|
||||
import defaultAvatar from '@/static/default-avatar.png'
|
||||
|
||||
const props = defineProps(['workType', 'tenantId'])
|
||||
const toast = useToast()
|
||||
const userStore = useUserStore()
|
||||
const paramsStore = useParamsStore()
|
||||
const paging = ref(null)
|
||||
const router = useRouter()
|
||||
const dataList = ref([])
|
||||
// 接口拿到的数据处理之后的
|
||||
const originData = ref([])
|
||||
const keyword = ref('')
|
||||
const dataSource = ref([])
|
||||
|
||||
const api = {
|
||||
appQueryUser: '/sys/user/appQueryUser',
|
||||
getMyGroupList: '/eoa/im/newApi/getMyGroupList',
|
||||
}
|
||||
const queryList = (pageNo, pageSize) => {
|
||||
const pararms = { pageNo, pageSize, tenantId: props.tenantId }
|
||||
let url
|
||||
if (props.workType) {
|
||||
url = api.getMyGroupList
|
||||
pararms['type'] = props.workType
|
||||
} else {
|
||||
url = api.appQueryUser
|
||||
}
|
||||
http
|
||||
.get(url, pararms)
|
||||
.then((res: any) => {
|
||||
if (res.success && res.result.length) {
|
||||
paging.value.complete(res.result)
|
||||
} else {
|
||||
paging.value.complete(false)
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
paging.value.complete(false)
|
||||
})
|
||||
}
|
||||
// 监听接口数据
|
||||
watch(dataList, () => {
|
||||
let result = handleResult(dataList.value)
|
||||
result = transformData(result)
|
||||
result.sort((a, b) => a.index.localeCompare(b.index))
|
||||
originData.value = [...result]
|
||||
dataSource.value = result
|
||||
})
|
||||
|
||||
// 搜索
|
||||
function handleSearch() {
|
||||
dataSource.value = []
|
||||
nextTick(() => {
|
||||
if (keyword.value) {
|
||||
dataSource.value = originData.value.filter((item) => {
|
||||
return item.data.some((inItem) => {
|
||||
return inItem.realname.indexOf(keyword.value) != -1
|
||||
})
|
||||
})
|
||||
} else {
|
||||
dataSource.value = originData.value
|
||||
}
|
||||
})
|
||||
}
|
||||
// 清除搜索条件
|
||||
function handleClear() {
|
||||
keyword.value = ''
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
const handleClick = (index: string, data: any) => {
|
||||
if (props.workType) {
|
||||
toast.warning('群组聊天暂未开发~')
|
||||
} else {
|
||||
paramsStore.setPageParams('personPage', { data })
|
||||
router.push({ name: 'personPage' })
|
||||
}
|
||||
}
|
||||
const handleChange = ({ option }) => {
|
||||
paging.value.reload()
|
||||
}
|
||||
const transformData = (data) => {
|
||||
const grouped = data.reduce((acc, item) => {
|
||||
const key = item.szm
|
||||
if (!acc[key]) {
|
||||
acc[key] = []
|
||||
}
|
||||
acc[key].push(item)
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
return Object.entries(grouped)
|
||||
.map(([index, data]) => ({ index, data }))
|
||||
.sort((a, b) => b.index.localeCompare(a.index))
|
||||
}
|
||||
const handleResult = (arr) => {
|
||||
let newArr = []
|
||||
arr.forEach((item) => {
|
||||
let { id, realname, avatar, username, phone, email, post, orgCodeTxt } = item
|
||||
//聊天通讯录把自己过滤掉
|
||||
if (username !== userStore.userInfo.username) {
|
||||
let pinYin = realname
|
||||
if (realname) {
|
||||
//TODO 判断汉字的位置
|
||||
if (/.*[\u4e00-\u9fa5]+.*$/.test(realname)) {
|
||||
pinYin = vPinyin.chineseToPinYin(realname)
|
||||
}
|
||||
}
|
||||
if (avatar) {
|
||||
avatar = getFileAccessHttpUrl(avatar)
|
||||
}
|
||||
let szm = pinYin.substr(0, 1)
|
||||
var numReg = /^[0-9]*$/
|
||||
var numRe = new RegExp(numReg)
|
||||
szm = !numRe.test(szm) ? szm.toUpperCase() : '#'
|
||||
newArr.push({
|
||||
id,
|
||||
realname,
|
||||
avatar,
|
||||
username,
|
||||
phone,
|
||||
email,
|
||||
post,
|
||||
orgCodeTxt,
|
||||
szm: szm,
|
||||
})
|
||||
}
|
||||
})
|
||||
return newArr
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
const tenantList = cache(TENANT_LIST)
|
||||
const result = tenantList?.map((item) => {
|
||||
return { key: item.id, title: item.name }
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wrap {
|
||||
height: 100%;
|
||||
}
|
||||
.z-paging-content {
|
||||
// :deep(.zp-paging-container) {
|
||||
// flex: none;
|
||||
// height: calc(100% - 42px);
|
||||
// .zp-paging-container-content {
|
||||
// height: 100%;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
:deep(.avatar) {
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
uni-text {
|
||||
&:first-child {
|
||||
font-size: 16px;
|
||||
color: #8799a3;
|
||||
}
|
||||
&:last-child {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.wraper {
|
||||
// height: 100%;
|
||||
height: calc(100vh - var(--window-top) - constant(safe-area-inset-bottom) - 140px);
|
||||
height: calc(100vh - var(--window-top) - env(safe-area-inset-bottom) - 140px);
|
||||
:deep(.wd-cell__right) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
125
src/pages-message/tenant/tenant.vue
Normal file
125
src/pages-message/tenant/tenant.vue
Normal file
@ -0,0 +1,125 @@
|
||||
<route lang="json5" type="page">
|
||||
{
|
||||
layout: 'default',
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
styleIsolation: 'apply-shared',
|
||||
},
|
||||
}
|
||||
</route>
|
||||
<template>
|
||||
<PageLayout :navTitle="title" backRouteName="message" routeMethod="pushTab">
|
||||
<wd-tabs customClass="" v-model="tabActive">
|
||||
<template v-for="(item, index) in tabList" :key="index">
|
||||
<wd-tab :title="item.title" :name="item.key">
|
||||
<workmate
|
||||
v-if="tabActive == '1'"
|
||||
:tenantId="tenantId"
|
||||
:key="reloadKey"
|
||||
:workType="workType"
|
||||
></workmate>
|
||||
<department v-if="tabActive == '2'" :tenantId="tenantId"></department>
|
||||
</wd-tab>
|
||||
</template>
|
||||
</wd-tabs>
|
||||
<template #navRight>
|
||||
<view
|
||||
class="cuIcon-filter font-size-20px color-white"
|
||||
@click="() => (conditionFilter.show = true)"
|
||||
></view>
|
||||
</template>
|
||||
<rightConditionFilter
|
||||
v-if="conditionFilter.show"
|
||||
v-bind="conditionFilter"
|
||||
@close="() => (conditionFilter.show = false)"
|
||||
@change="handleChange"
|
||||
></rightConditionFilter>
|
||||
</PageLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onHide, onLoad, onReady } from '@dcloudio/uni-app'
|
||||
import { ref } from 'vue'
|
||||
import workmate from './components/workmate.vue'
|
||||
import department from './components/department.vue'
|
||||
import rightConditionFilter from '@/components/RightConditionFilter/RightConditionFilter.vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'tenant',
|
||||
options: {
|
||||
// apply-shared:当前页面样式会影响到子组件样式.(小程序)
|
||||
// shared:当前页面样式影响到子组件,子组件样式也会影响到当前页面.(小程序)
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const reloadKey = ref(1)
|
||||
const tabList = ref([
|
||||
{ key: '1', title: '全部' },
|
||||
{ key: '2', title: '按部门' },
|
||||
])
|
||||
const conditionFilter = reactive({
|
||||
show: false,
|
||||
checked: 'all',
|
||||
options: [
|
||||
{ key: 'all', title: '所有同事' },
|
||||
{ key: 'group', title: '群组' },
|
||||
],
|
||||
})
|
||||
const tabActive = ref('2')
|
||||
const tenantId = ref()
|
||||
const title = ref()
|
||||
const workType = ref('')
|
||||
const handleChange = ({ option }) => {
|
||||
conditionFilter.checked = option.key
|
||||
if (option.key == 'all') {
|
||||
tabList.value[1]['title'] = '按部门'
|
||||
workType.value = ''
|
||||
} else if (option.key == 'group') {
|
||||
tabList.value[1]['title'] = '我创建的'
|
||||
if (tabActive.value == '1') {
|
||||
workType.value = 'allGroup'
|
||||
} else {
|
||||
workType.value = 'createdGroup'
|
||||
}
|
||||
}
|
||||
tabList.value = [...tabList.value]
|
||||
|
||||
// 重新加载
|
||||
reloadKey.value = Math.random()
|
||||
}
|
||||
const init = () => {}
|
||||
init()
|
||||
onLoad((options) => {
|
||||
tenantId.value = options.id
|
||||
title.value = options.title
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.wd-tabs) {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.wd-tabs__nav {
|
||||
border-bottom: 1px solid #f1f1f1;
|
||||
}
|
||||
.wd-tabs__container {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
.wd-tabs__body {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
:deep(.wd-tab) {
|
||||
.wd-tab__body {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
181
src/pages-sub/online/components/onlineTableCell.vue
Normal file
181
src/pages-sub/online/components/onlineTableCell.vue
Normal file
@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<view class="onlineTableCell">
|
||||
<!--图片-->
|
||||
<template v-if="column?.scopedSlots?.customRender === 'imgSlot'">
|
||||
<template v-if="record[column.dataIndex]">
|
||||
<wd-img
|
||||
width="30"
|
||||
height="30"
|
||||
:src="getFirstImg(record[column.dataIndex])"
|
||||
@click="handleClickImg"
|
||||
></wd-img>
|
||||
<ImgPreview
|
||||
v-if="imgPreview.show"
|
||||
:urls="imgPreview.urls"
|
||||
@close="() => (imgPreview.show = false)"
|
||||
></ImgPreview>
|
||||
</template>
|
||||
<template v-else>
|
||||
<text>无图片</text>
|
||||
</template>
|
||||
</template>
|
||||
<!--下载-->
|
||||
<template v-else-if="column?.scopedSlots?.customRender === 'fileSlot'">
|
||||
<template v-if="record[column.dataIndex]">
|
||||
<wd-button @click="handleDownload(record[column.dataIndex])">下载</wd-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<text>无文件</text>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else-if="column?.scopedSlots?.customRender === 'htmlSlot'">
|
||||
<!-- 增加富文本控件配置href跳转 -->
|
||||
<template v-if="column.fieldHref">
|
||||
<text class="ellipsis-2">暂不支持</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<rich-text :nodes="record[column.dataIndex]"></rich-text>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else-if="column?.scopedSlots?.customRender === 'pcaSlot'">
|
||||
<text class="ellipsis-2">{{ getPcaText(record[column.dataIndex]) || ' '}}</text>
|
||||
</template>
|
||||
<template v-else-if="column?.scopedSlots?.customRender === 'dateSlot'">
|
||||
<text class="ellipsis-2">{{ getFormatDate(record[column.dataIndex], column) || ' '}}</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<text class="ellipsis-2">{{ renderVal(record, column) || ' ' }}</text>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getFormatDate, filterMultiDictText } from '../utils/index'
|
||||
import { isString } from '@/utils/is'
|
||||
import { getFileAccessHttpUrl } from '@/common/uitls'
|
||||
import { getAreaTextByCode } from '@/common/areaData/Area'
|
||||
defineOptions({
|
||||
name: 'onlineTableCell',
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const props = defineProps({
|
||||
columnsInfo: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
column: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
record: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
})
|
||||
const imgPreview = ref({
|
||||
show: false,
|
||||
urls: [],
|
||||
})
|
||||
// 下载
|
||||
const handleDownload = (text) => {
|
||||
uni.downloadFile({
|
||||
url: text,
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
console.log('下载成功')
|
||||
console.log(res);
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
// 省市区
|
||||
const getPcaText = (code) => {
|
||||
if (!code) {
|
||||
return ''
|
||||
}
|
||||
return getAreaTextByCode(code)
|
||||
}
|
||||
// 列表只显示第一张图
|
||||
const getFirstImg = (text) => {
|
||||
if (isString(text)) {
|
||||
var imgs = text.split(',')
|
||||
return getFileAccessHttpUrl(imgs[0])
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
// 点击图时
|
||||
const handleClickImg = () => {
|
||||
imgPreview.value.show = true
|
||||
}
|
||||
// 渲染值
|
||||
const renderVal = (record, column) => {
|
||||
const { customRender, hrefSlotName, dataIndex, fieldType } = column
|
||||
let text = record[dataIndex]
|
||||
if (['date', 'Date'].includes(column['fieldType'])) {
|
||||
// online报表中类型配置为日期(yyyy-MM-dd ),但是实际展示为日期时间格式(yyyy-MM-dd HH:mm:ss)
|
||||
if (!text) {
|
||||
return ''
|
||||
}
|
||||
if (text.length > 10) {
|
||||
return text.substring(0, 10)
|
||||
}
|
||||
return text
|
||||
} else if (['popup_dict'].includes(column['fieldType'])) {
|
||||
const dict = record[dataIndex + '_dictText']
|
||||
if (dict != undefined) {
|
||||
return record[dataIndex + '_dictText']
|
||||
}
|
||||
return text
|
||||
} else if (customRender) {
|
||||
// 字典
|
||||
let dictCode = customRender as string
|
||||
let replaceFlag = '_replace_text_'
|
||||
let value = text
|
||||
// 如果 dictCode 有值,就进行字典转换
|
||||
if (dictCode) {
|
||||
if (dictCode.startsWith(replaceFlag)) {
|
||||
let textFieldName = dictCode.replace(replaceFlag, '')
|
||||
value = record[textFieldName]
|
||||
} else {
|
||||
value = filterMultiDictText(unref(props.columnsInfo.dictOptions)[dictCode], text + '')
|
||||
}
|
||||
}
|
||||
// 扩展参数设置列的内容长度
|
||||
if (column.showLength) {
|
||||
if (value && value.length > column.showLength) {
|
||||
value = value.substr(0, column.showLength) + '...'
|
||||
}
|
||||
}
|
||||
return value
|
||||
} else {
|
||||
return text
|
||||
}
|
||||
}
|
||||
// 初始化
|
||||
const init = () => {
|
||||
const field = props.column.dataIndex
|
||||
if (props.column?.scopedSlots?.customRender === 'imgSlot') {
|
||||
const text = props.record[field]
|
||||
if (isString(text)) {
|
||||
imgPreview.value.urls = text.split(',').map((item) => getFileAccessHttpUrl(item))
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
init()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.wd-button) {
|
||||
--wot-button-medium-height: 30px;
|
||||
--wot-button-medium-fs: 12px;
|
||||
--wot-button-medium-padding: 8px;
|
||||
&.is-medium.is-round {
|
||||
min-width: 80px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
86
src/pages-sub/online/hooks/useOnline.ts
Normal file
86
src/pages-sub/online/hooks/useOnline.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { http } from '@/utils/http'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { useRouter } from '@/plugin/uni-mini-router'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { useParamsStore } from '@/store/page-params'
|
||||
|
||||
export function useOnline() {
|
||||
const toast = useToast()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const globalData = getApp().globalData
|
||||
const { systemInfo } = globalData
|
||||
const { safeArea } = systemInfo
|
||||
const paramsStore = useParamsStore()
|
||||
const columns = ref([])
|
||||
const columnsInfo = ref({})
|
||||
const pageNo = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const pageTotal = ref(1)
|
||||
let pageParams = paramsStore.getPageParams('onlineCard')?.data ?? {}
|
||||
const dataList = ref([])
|
||||
|
||||
const queryParams = () => {
|
||||
return {
|
||||
pageNo: pageNo.value,
|
||||
pageSize: pageSize.value,
|
||||
order: 'asc',
|
||||
column: 'id',
|
||||
hasQuery: true,
|
||||
}
|
||||
}
|
||||
// 获取列字段
|
||||
const getColumns = () => {
|
||||
return new Promise<void>((resove, reject) => {
|
||||
if (columns.value.length) {
|
||||
resove()
|
||||
return
|
||||
}
|
||||
const analysis = (data) => {
|
||||
const len = data.length
|
||||
const maxShowColumn = 3
|
||||
let space = 1
|
||||
if (len == 1) {
|
||||
space = 2
|
||||
}
|
||||
const width = safeArea.width / (len > maxShowColumn ? maxShowColumn : len) - space
|
||||
columns.value = data.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
prop: item.dataIndex,
|
||||
align: item.align,
|
||||
label: item.title,
|
||||
width,
|
||||
}
|
||||
})
|
||||
}
|
||||
http
|
||||
.get(`/online/cgform/api/getColumns/${pageParams.id}`)
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
if (res.result?.columns?.length) {
|
||||
columnsInfo.value = res.result
|
||||
analysis(res.result.columns)
|
||||
resove()
|
||||
}
|
||||
} else {
|
||||
toast.warning(res.message)
|
||||
reject()
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
toast.error('加载列头失败~')
|
||||
reject()
|
||||
})
|
||||
})
|
||||
}
|
||||
return {
|
||||
toast,
|
||||
router,
|
||||
userStore,
|
||||
paramsStore,
|
||||
columns,
|
||||
columnsInfo,
|
||||
}
|
||||
}
|
||||
249
src/pages-sub/online/online.vue
Normal file
249
src/pages-sub/online/online.vue
Normal file
@ -0,0 +1,249 @@
|
||||
<route lang="json5" type="page">
|
||||
{
|
||||
layout: 'default',
|
||||
style: {
|
||||
navigationBarTitleText: 'online',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<PageLayout navTitle="online表单开发" backRouteName="index" routeMethod="pushTab">
|
||||
<view class="wrap">
|
||||
<z-paging
|
||||
ref="paging"
|
||||
:fixed="false"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
:default-page-size="20"
|
||||
>
|
||||
<template #top>
|
||||
<wd-search
|
||||
hide-cancel
|
||||
placeholder="请输入表描述"
|
||||
v-model.trim="keyword"
|
||||
@search="handleSearch"
|
||||
@clear="handleClear"
|
||||
/>
|
||||
</template>
|
||||
<template v-for="(item, index) in dataList" :key="item.id">
|
||||
<template v-if="item.tableType != 3">
|
||||
<wd-swipe-action>
|
||||
<view class="list" @click="handleGo(item)">
|
||||
<view
|
||||
class="cIcon"
|
||||
:style="{ 'background-color': getBackgroundColor(item, index) }"
|
||||
>
|
||||
<view class="u-iconfont u-icon-table"></view>
|
||||
</view>
|
||||
<view class="tableTxt ellipsis">{{ item.tableTxt }}</view>
|
||||
<view class="createTime ellipsis">{{ item.createTime.substring(0, 10) }}</view>
|
||||
</view>
|
||||
<template #right>
|
||||
<view class="action">
|
||||
<view class="button" @click="handleAction('del', item)">删除</view>
|
||||
<view class="button" @click="handleAction('remove', item)">移除</view>
|
||||
</view>
|
||||
</template>
|
||||
</wd-swipe-action>
|
||||
</template>
|
||||
</template>
|
||||
</z-paging>
|
||||
</view>
|
||||
</PageLayout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { http } from '@/utils/http'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { useRouter } from '@/plugin/uni-mini-router'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { useParamsStore } from '@/store/page-params'
|
||||
import { getRandomColor } from '@/common/uitls'
|
||||
|
||||
defineOptions({
|
||||
name: 'online',
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const paramsStore = useParamsStore()
|
||||
const paging = ref(null)
|
||||
const dataList: any = ref([])
|
||||
const keyword = ref('')
|
||||
const itemBgColor = []
|
||||
// 接口拿到的数据处理之后的
|
||||
|
||||
const handleGo = (item) => {
|
||||
if (item.tableType === 3) {
|
||||
toast.warning('附表无列表页~')
|
||||
} else {
|
||||
paramsStore.setPageParams('onlineCard', { data: item })
|
||||
router.push({ name: 'onlineCard' })
|
||||
}
|
||||
}
|
||||
// 清除搜索条件
|
||||
function handleClear() {
|
||||
keyword.value = ''
|
||||
handleSearch()
|
||||
}
|
||||
// 搜索
|
||||
function handleSearch() {
|
||||
queryList(1, 10)
|
||||
}
|
||||
const handleAction = (val, item) => {
|
||||
if (val == 'del') {
|
||||
http
|
||||
.delete('/online/cgform/head/delete', { id: item })
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
toast.success('删除成功~')
|
||||
paging.value.reload()
|
||||
} else {
|
||||
toast.warning('删除失败~')
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
toast.error('删除失败~')
|
||||
})
|
||||
} else if ((val = 'remove')) {
|
||||
http
|
||||
.delete('/online/cgform/head/removeRecord', { id: item })
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
toast.success('移除成功~')
|
||||
paging.value.reload()
|
||||
} else {
|
||||
toast.warning('移除失败~')
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
toast.error('移除失败~')
|
||||
})
|
||||
}
|
||||
}
|
||||
const getParams = ({ pageNo, pageSize }) => {
|
||||
const params: any = {
|
||||
pageNo,
|
||||
pageSize,
|
||||
order: 'desc',
|
||||
column: 'createTime',
|
||||
}
|
||||
if (keyword.value.length) {
|
||||
params.tableTxt = `*${keyword.value}*`
|
||||
}
|
||||
return params
|
||||
}
|
||||
const queryList = (pageNo, pageSize) => {
|
||||
const params = getParams({ pageNo, pageSize })
|
||||
http
|
||||
.get('/online/cgform/head/list', { ...params })
|
||||
.then((res: any) => {
|
||||
if (res.success && res.result?.records) {
|
||||
if (pageNo === 1) {
|
||||
dataList.value = []
|
||||
}
|
||||
paging.value.complete(res.result.records)
|
||||
} else {
|
||||
paging.value.complete(false)
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
paging.value.complete(false)
|
||||
})
|
||||
}
|
||||
const getType = (record) => {
|
||||
const type = { 1: '单表', 2: '主表', 3: '附表' }
|
||||
let tbTypeText = type[record.tableType]
|
||||
// if (record.isTree === 'Y') {
|
||||
// tbTypeText += '(树)'
|
||||
// }
|
||||
// if (record.themeTemplate === 'innerTable') {
|
||||
// tbTypeText += '(内嵌)'
|
||||
// } else if (record.themeTemplate === 'erp') {
|
||||
// tbTypeText += '(ERP)'
|
||||
// } else if (record.themeTemplate === 'tab') {
|
||||
// tbTypeText += '(TAB)'
|
||||
// }
|
||||
return tbTypeText
|
||||
}
|
||||
const getBackgroundColor = (item, index) => {
|
||||
return itemBgColor[index % itemBgColor.length]
|
||||
}
|
||||
for (let i = 0; i < 50; i++) {
|
||||
itemBgColor.push(getRandomColor())
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
//
|
||||
:deep(.wd-search) {
|
||||
border-bottom: 1px solid #f4f2f2;
|
||||
}
|
||||
.wd-swipe-action {
|
||||
&:first-child {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
.list {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding: 16px 12px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 10px;
|
||||
.cIcon {
|
||||
text-align: center;
|
||||
line-height: 24px;
|
||||
color: #fff;
|
||||
margin-right: 8px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
.u-iconfont {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.tableTxt {
|
||||
flex: 1;
|
||||
margin-right: 40px;
|
||||
}
|
||||
.createTime {
|
||||
text-align: right;
|
||||
width: 75px;
|
||||
font-size: 12px;
|
||||
color: #919191;
|
||||
}
|
||||
}
|
||||
.action {
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
color: #fff;
|
||||
&:first-child {
|
||||
background-color: #fa4350;
|
||||
}
|
||||
&:last-child {
|
||||
background-color: #f0883a;
|
||||
}
|
||||
}
|
||||
}
|
||||
.wrap {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
264
src/pages-sub/online/onlineCard.vue
Normal file
264
src/pages-sub/online/onlineCard.vue
Normal file
@ -0,0 +1,264 @@
|
||||
<route lang="json5" type="page">
|
||||
{
|
||||
layout: 'default',
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
}
|
||||
</route>
|
||||
<template>
|
||||
<PageLayout backRouteName="online" navTitle="online在线表单">
|
||||
<view class="wrap">
|
||||
<z-paging
|
||||
ref="paging"
|
||||
:fixed="false"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
:default-page-size="15"
|
||||
>
|
||||
<template v-for="(item, index) in dataList" :key="item.id">
|
||||
<wd-swipe-action>
|
||||
<view class="list" @click="handleEdit(item)">
|
||||
<template v-for="(cItem, cIndex) in columns" :key="index">
|
||||
<view v-if="cIndex < 3" class="box" :style="getBoxStyle">
|
||||
<view class="field ellipsis">{{ cItem['title'] }}</view>
|
||||
<view class="value text-grey">
|
||||
<onlineTableCell
|
||||
:columnsInfo="columnsInfo"
|
||||
:record="item"
|
||||
:column="cItem"
|
||||
:key="item.id"
|
||||
></onlineTableCell>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
<template #right>
|
||||
<view class="action">
|
||||
<view class="button" @click="handleAction('del', item)">删除</view>
|
||||
<!-- <view class="button" @click="handleAction('view', item)">查看</view> -->
|
||||
</view>
|
||||
</template>
|
||||
</wd-swipe-action>
|
||||
</template>
|
||||
</z-paging>
|
||||
<view class="add u-iconfont u-icon-add" @click="handleAdd"></view>
|
||||
<view class="goTable u-iconfont u-icon-table" @click="handleGoTable"></view>
|
||||
</view>
|
||||
</PageLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { http } from '@/utils/http'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { useRouter } from '@/plugin/uni-mini-router'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { useParamsStore } from '@/store/page-params'
|
||||
import onlineTableCell from './components/onlineTableCell.vue'
|
||||
defineOptions({
|
||||
name: 'onlineCard',
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const toast = useToast()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const paramsStore = useParamsStore()
|
||||
const globalData = getApp().globalData
|
||||
const { systemInfo } = globalData
|
||||
const { safeArea } = systemInfo
|
||||
const paging = ref(null)
|
||||
const columns = ref([])
|
||||
const columnsInfo = ref({})
|
||||
const pageNo = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const pageTotal = ref(1)
|
||||
let pageParams = paramsStore.getPageParams('onlineCard')?.data ?? {}
|
||||
const dataList = ref([])
|
||||
const getBoxStyle = computed(() => {
|
||||
let len = columns.value.length
|
||||
if (len > 3) len = 3
|
||||
return { width: `calc(${100 / len}% - 5px)` }
|
||||
})
|
||||
const queryParams = () => {
|
||||
return {
|
||||
pageNo: pageNo.value,
|
||||
pageSize: pageSize.value,
|
||||
order: 'asc',
|
||||
column: 'id',
|
||||
hasQuery: true,
|
||||
}
|
||||
}
|
||||
const getColumns = () => {
|
||||
return new Promise<void>((resove, reject) => {
|
||||
if (columns.value.length) {
|
||||
resove()
|
||||
return
|
||||
}
|
||||
const analysis = (data) => {
|
||||
const len = data.length
|
||||
const maxShowColumn = 3
|
||||
let space = 1
|
||||
if (len == 1) {
|
||||
space = 2
|
||||
}
|
||||
const width = safeArea.width / (len > maxShowColumn ? maxShowColumn : len) - space
|
||||
columns.value = data.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
prop: item.dataIndex,
|
||||
align: item.align,
|
||||
label: item.title,
|
||||
width,
|
||||
}
|
||||
})
|
||||
}
|
||||
http
|
||||
.get(`/online/cgform/api/getColumns/${pageParams.id}`)
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
if (res.result?.columns?.length) {
|
||||
columnsInfo.value = res.result
|
||||
analysis(res.result.columns)
|
||||
resove()
|
||||
}
|
||||
} else {
|
||||
toast.warning(res.message)
|
||||
reject()
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
toast.error('加载列头失败~')
|
||||
reject()
|
||||
})
|
||||
})
|
||||
}
|
||||
const getData = () => {
|
||||
http
|
||||
.get(`/online/cgform/api/getData/${pageParams.id}`, { ...queryParams() })
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
paging.value.complete(res.result?.records ?? [])
|
||||
} else {
|
||||
toast.warning(res.message)
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
toast.error('加载表格数据失败~')
|
||||
})
|
||||
}
|
||||
const handleAction = (val, item) => {
|
||||
if (val == 'del') {
|
||||
http.delete(`/online/cgform/api/form/${pageParams.id}/${item.id}`).then((res) => {
|
||||
toast.success('删除成功~')
|
||||
paging.value.reload()
|
||||
})
|
||||
}
|
||||
}
|
||||
const queryList = (_pageNo, _pageSize) => {
|
||||
pageNo.value = _pageNo
|
||||
pageSize.value = _pageSize
|
||||
getColumns().then(() => {
|
||||
getData()
|
||||
})
|
||||
}
|
||||
// go 新增页
|
||||
const handleAdd = () => {
|
||||
router.push({
|
||||
name: 'onlineAdd',
|
||||
params: {
|
||||
desformCode: pageParams.tableName,
|
||||
desformName: pageParams.tableTxt,
|
||||
backRouteName: 'onlineCard',
|
||||
},
|
||||
})
|
||||
}
|
||||
// go table页
|
||||
const handleGoTable = (params) => {
|
||||
paramsStore.setPageParams('onlineTable', { data: pageParams })
|
||||
router.push({ name: 'onlineTable' })
|
||||
}
|
||||
//go 编辑页
|
||||
const handleEdit = (record) => {
|
||||
router.push({
|
||||
name: 'onlineEdit',
|
||||
params: {
|
||||
desformCode: pageParams.tableName,
|
||||
desformName: pageParams.tableTxt,
|
||||
id: record.id,
|
||||
backRouteName: 'onlineCard',
|
||||
},
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
// 监听刷新列表事件
|
||||
uni.$on('refreshList', () => {
|
||||
getData()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wrap {
|
||||
height: 100%;
|
||||
}
|
||||
:deep(.wd-swipe-action) {
|
||||
margin-top: 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
.list {
|
||||
padding: 10px 10px;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.box {
|
||||
width: 33%;
|
||||
.field {
|
||||
margin-bottom: 10px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.action {
|
||||
width: 60px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
color: #fff;
|
||||
&:first-child {
|
||||
background-color: #fa4350;
|
||||
}
|
||||
&:last-child {
|
||||
// background-color: #f0883a;
|
||||
}
|
||||
}
|
||||
}
|
||||
.add,
|
||||
.goTable {
|
||||
height: 70upx;
|
||||
width: 70upx;
|
||||
text-align: center;
|
||||
line-height: 70upx;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
position: fixed;
|
||||
bottom: 80upx;
|
||||
right: 30upx;
|
||||
box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.1);
|
||||
color: #666;
|
||||
}
|
||||
.goTable {
|
||||
bottom: 180upx;
|
||||
}
|
||||
</style>
|
||||
257
src/pages-sub/online/onlineTable.vue
Normal file
257
src/pages-sub/online/onlineTable.vue
Normal file
@ -0,0 +1,257 @@
|
||||
<route lang="json5" type="page">
|
||||
{
|
||||
layout: 'default',
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<PageLayout backRouteName="online" navTitle="online在线表单">
|
||||
<wd-table :index="tableIndex" :data="dataList" @row-click="handleRow">
|
||||
<template v-for="(item, index) in columns" :key="item.prop">
|
||||
<wd-table-col
|
||||
:prop="item.prop"
|
||||
:label="item.label"
|
||||
:width="item.width"
|
||||
:fixed="item.fixed"
|
||||
:align="item.align"
|
||||
:sortable="item.sortable"
|
||||
>
|
||||
<template #value="{ row, index }">
|
||||
<onlineTableCell
|
||||
:columnsInfo="columnsInfo"
|
||||
:record="row"
|
||||
:column="item"
|
||||
@longpress.prevent="handleLongPress(row)"
|
||||
></onlineTableCell>
|
||||
</template>
|
||||
</wd-table-col>
|
||||
</template>
|
||||
</wd-table>
|
||||
<wd-status-tip v-if="dataList.length == 0" image="content" tip="暂无内容" />
|
||||
<wd-pagination
|
||||
v-model="pageNo"
|
||||
:total="pageTotal"
|
||||
:page-size="pageSize"
|
||||
@change="handlePaginChange"
|
||||
show-icon
|
||||
></wd-pagination>
|
||||
<view class="add u-iconfont u-icon-add" @click="handleGo"></view>
|
||||
</PageLayout>
|
||||
<BottomOperate
|
||||
v-if="bottomOperatePopup.show"
|
||||
v-bind="bottomOperatePopup"
|
||||
@close="() => (bottomOperatePopup.show = false)"
|
||||
@change="handleChange"
|
||||
></BottomOperate>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { http } from '@/utils/http'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { useRouter } from '@/plugin/uni-mini-router'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { useParamsStore } from '@/store/page-params'
|
||||
import onlineTableCell from './components/onlineTableCell.vue'
|
||||
defineOptions({
|
||||
name: 'onlineTable',
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const toast = useToast()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const paramsStore = useParamsStore()
|
||||
const globalData = getApp().globalData
|
||||
const { systemInfo } = globalData
|
||||
const { safeArea } = systemInfo
|
||||
|
||||
const columns = ref([])
|
||||
const columnsInfo = ref({})
|
||||
const tableIndex = ref(false)
|
||||
const pageNo = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const pageTotal = ref(1)
|
||||
let pageParams: any = {}
|
||||
let rowIndex: any = ref(0)
|
||||
// 底部操作
|
||||
const bottomOperatePopup = reactive({
|
||||
show: false,
|
||||
title: '操作',
|
||||
data: {},
|
||||
options: [
|
||||
{ key: 'edit', icon: 'edit', label: '编辑' },
|
||||
{ key: 'detail', icon: 'view', label: '查看' },
|
||||
{ key: 'delete', icon: 'delete', label: '删除', color: 'red' },
|
||||
],
|
||||
})
|
||||
const queryParams = () => {
|
||||
return {
|
||||
pageNo: pageNo.value,
|
||||
pageSize: pageSize.value,
|
||||
order: 'asc',
|
||||
column: 'id',
|
||||
hasQuery: true,
|
||||
}
|
||||
}
|
||||
const dataList = ref([])
|
||||
const getColumns = () => {
|
||||
return new Promise<void>((resove, reject) => {
|
||||
const analysis = (data) => {
|
||||
const len = data.length
|
||||
const maxShowColumn = 3
|
||||
let space = 1
|
||||
if (len == 1) {
|
||||
space = 2
|
||||
}
|
||||
const width = safeArea.width / (len > maxShowColumn ? maxShowColumn : len) - space
|
||||
columns.value = data.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
prop: item.dataIndex,
|
||||
align: item.align,
|
||||
label: item.title,
|
||||
width,
|
||||
}
|
||||
})
|
||||
}
|
||||
http
|
||||
.get(`/online/cgform/api/getColumns/${pageParams.id}`)
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
if (res.result?.columns?.length) {
|
||||
columnsInfo.value = res.result
|
||||
analysis(res.result.columns)
|
||||
}
|
||||
} else {
|
||||
toast.warning(res.message)
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
toast.error('加载列头失败~')
|
||||
})
|
||||
})
|
||||
}
|
||||
const getData = () => {
|
||||
http
|
||||
.get(`/online/cgform/api/getData/${pageParams.id}`, { ...queryParams() })
|
||||
.then((res: any) => {
|
||||
if (res.success) {
|
||||
dataList.value = res.result?.records ?? []
|
||||
pageTotal.value = res.result.total
|
||||
} else {
|
||||
toast.warning(res.message)
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
toast.error('加载表格数据失败~')
|
||||
})
|
||||
}
|
||||
const handlePaginChange = ({ value }) => {
|
||||
pageNo.value = value
|
||||
getData()
|
||||
}
|
||||
//go 新增页
|
||||
const handleGo = () => {
|
||||
router.push({
|
||||
name: 'onlineAdd',
|
||||
params: { desformCode: pageParams.tableName, desformName: pageParams.tableTxt },
|
||||
})
|
||||
}
|
||||
//go 编辑页
|
||||
const handleEdit = (record) => {
|
||||
router.push({
|
||||
name: 'onlineEdit',
|
||||
params: { desformCode: pageParams.tableName, desformName: pageParams.tableTxt, id: record.id },
|
||||
})
|
||||
}
|
||||
//go 编辑页
|
||||
const handleView = (record) => {
|
||||
router.push({
|
||||
name: 'onlineDetail',
|
||||
params: { desformCode: pageParams.tableName, desformName: pageParams.tableTxt, id: record.id },
|
||||
})
|
||||
}
|
||||
//长按操作
|
||||
const handleLongPress = (item) => {
|
||||
bottomOperatePopup.show = true
|
||||
bottomOperatePopup.data = item
|
||||
}
|
||||
//操作切换
|
||||
const handleChange = ({ option, data }) => {
|
||||
if (option.key === 'edit') {
|
||||
handleEdit(data)
|
||||
} else if (option.key === 'delete') {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除吗?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
http.delete(`/online/cgform/api/form/${pageParams.id}/${data.id}`).then((res) => {
|
||||
toast.success('删除成功~')
|
||||
getData()
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err)
|
||||
},
|
||||
})
|
||||
} else if (option.key === 'detail') {
|
||||
handleView(data)
|
||||
}
|
||||
}
|
||||
const handleRow = ({ rowIndex }) => {
|
||||
handleEdit(dataList.value[rowIndex])
|
||||
}
|
||||
const init = () => {
|
||||
pageParams = paramsStore.getPageParams('onlineTable')?.data ?? {}
|
||||
console.log('pageParams:', pageParams)
|
||||
getColumns()
|
||||
getData()
|
||||
}
|
||||
init()
|
||||
|
||||
onMounted(() => {
|
||||
// 监听刷新列表事件
|
||||
uni.$on('refreshList', () => {
|
||||
getData()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
//
|
||||
.add {
|
||||
height: 70upx;
|
||||
width: 70upx;
|
||||
text-align: center;
|
||||
line-height: 70upx;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
position: fixed;
|
||||
bottom: 80upx;
|
||||
right: 30upx;
|
||||
box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.1);
|
||||
color: #666;
|
||||
}
|
||||
:deep(.wd-table__content) {
|
||||
.wot-theme-light {
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.wd-table) {
|
||||
--wot-table-font-size: 14px;
|
||||
.wd-table__body {
|
||||
--wot-table-color: var(--color-gray);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
64
src/pages-sub/online/utils/index.ts
Normal file
64
src/pages-sub/online/utils/index.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { getWeekMonthQuarterYear } from '@/common/uitls'
|
||||
|
||||
/**
|
||||
* 日期格式化
|
||||
* @param text
|
||||
*/
|
||||
export function getFormatDate(text, column) {
|
||||
if (!text) {
|
||||
return ''
|
||||
}
|
||||
let a = text
|
||||
if (a.length > 10) {
|
||||
a = a.substring(0, 10)
|
||||
}
|
||||
let fieldExtendJson = column?.fieldExtendJson
|
||||
if (fieldExtendJson) {
|
||||
fieldExtendJson = JSON.parse(fieldExtendJson)
|
||||
if (fieldExtendJson.picker && fieldExtendJson.picker != 'default') {
|
||||
const result = getWeekMonthQuarterYear(a)
|
||||
return result[fieldExtendJson.picker]
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典值替换文本通用方法(多选)
|
||||
* @param dictOptions 字典数组
|
||||
* @param text 字典值
|
||||
* @return String
|
||||
*/
|
||||
export function filterMultiDictText(dictOptions, text) {
|
||||
//js “!text” 认为0为空,所以做提前处理
|
||||
if (text === 0 || text === '0') {
|
||||
if (dictOptions) {
|
||||
for (let dictItem of dictOptions) {
|
||||
if (text == dictItem.value) {
|
||||
return dictItem.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!text || text == 'undefined' || text == 'null' || !dictOptions || dictOptions.length == 0) {
|
||||
return ''
|
||||
}
|
||||
let re = ''
|
||||
text = text.toString()
|
||||
let arr = text.split(',')
|
||||
dictOptions.forEach(function (option) {
|
||||
if (option) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i] === option.value) {
|
||||
re += option.text + ','
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if (re == '') {
|
||||
return text
|
||||
}
|
||||
return re.substring(0, re.length - 1)
|
||||
}
|
||||
89
src/pages-user/location/location.vue
Normal file
89
src/pages-user/location/location.vue
Normal file
@ -0,0 +1,89 @@
|
||||
<route lang="json5" type="page">
|
||||
{
|
||||
layout: 'default',
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<PageLayout navTitle="定位" backRouteName="people" routeMethod="pushTab">
|
||||
<map
|
||||
style="width: 100%; height: 100%"
|
||||
:latitude="latitude"
|
||||
:longitude="longitude"
|
||||
:markers="marker"
|
||||
:scale="scale"
|
||||
></map>
|
||||
</PageLayout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { onShow, onHide, onLoad, onReady } from '@dcloudio/uni-app'
|
||||
|
||||
const latitude = ref(40.009704)
|
||||
const longitude = ref(116.374999)
|
||||
const marker = reactive([
|
||||
{
|
||||
id: 0,
|
||||
latitude: latitude.value, //纬度
|
||||
longitude: longitude.value, //经度
|
||||
iconPath: '/static/location.png', //显示的图标
|
||||
rotate: 0, // 旋转度数
|
||||
width: 20, //宽
|
||||
height: 20, //高
|
||||
title: '你在哪了', //标注点名
|
||||
alpha: 0.5, //透明度
|
||||
/* label:{//为标记点旁边增加标签 //因背景颜色H5不支持
|
||||
content:'北京国炬公司',//文本
|
||||
color:'red',//文本颜色
|
||||
fontSize:24,//文字大小
|
||||
x:5,//label的坐标,原点是 marker 对应的经纬度
|
||||
y:1,//label的坐标,原点是 marker 对应的经纬度
|
||||
borderWidth:12,//边框宽度
|
||||
borderColor:'pink',//边框颜色
|
||||
borderRadius:20,//边框圆角
|
||||
bgColor:'black',//背景色
|
||||
padding:5,//文本边缘留白
|
||||
textAlign:'right'//文本对齐方式。
|
||||
}, */
|
||||
callout: {
|
||||
//自定义标记点上方的气泡窗口 点击有效
|
||||
content: '北京国炬公司', //文本
|
||||
color: '#ffffff', //文字颜色
|
||||
fontSize: 14, //文本大小
|
||||
borderRadius: 2, //边框圆角
|
||||
bgColor: '#00c16f', //背景颜色
|
||||
display: 'ALWAYS', //常显
|
||||
},
|
||||
// anchor:{//经纬度在标注图标的锚点,默认底边中点
|
||||
// x:0, 原点为给出的经纬度
|
||||
// y:0,
|
||||
// }
|
||||
},
|
||||
])
|
||||
const scale = 16
|
||||
|
||||
const getLocation = () => {
|
||||
uni.getLocation({
|
||||
type: 'gcj02',
|
||||
success: function (res) {
|
||||
console.log('当前位置的经度:' + res.longitude)
|
||||
console.log('当前位置的纬度:' + res.latitude)
|
||||
},
|
||||
fail: function (res) {
|
||||
console.log('当前位置的经度')
|
||||
},
|
||||
})
|
||||
}
|
||||
onLoad(() => {
|
||||
getLocation()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
//
|
||||
</style>
|
||||
124
src/pages-user/userEdit/components/avatar.vue
Normal file
124
src/pages-user/userEdit/components/avatar.vue
Normal file
@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<view class="wrap">
|
||||
<template v-if="imgSrc">
|
||||
<view class="showArea">
|
||||
<view class="iconBox" @click="handleDel">
|
||||
<view class="cuIcon-close"></view>
|
||||
</view>
|
||||
<wd-img
|
||||
:radius="4"
|
||||
enable-preview
|
||||
label="头像"
|
||||
width="60px"
|
||||
height="60px"
|
||||
:src="imgSrc"
|
||||
></wd-img>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="uploadArea" @click="handleUpload">
|
||||
<view class="iconBox">
|
||||
<view class="cuIcon-cameraadd"></view>
|
||||
</view>
|
||||
</div>
|
||||
</template>
|
||||
<wd-message-box></wd-message-box>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useMessage, useToast } from 'wot-design-uni'
|
||||
import { computed, watch } from 'vue'
|
||||
import { getEnvBaseUrl } from '@/utils/index'
|
||||
|
||||
defineOptions({
|
||||
name: 'avatar',
|
||||
options: {
|
||||
// apply-shared:当前页面样式会影响到子组件样式.(小程序)
|
||||
// shared:当前页面样式影响到子组件,子组件样式也会影响到当前页面.(小程序)
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const props = defineProps(['modelValue'])
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const toast = useToast()
|
||||
const message = useMessage()
|
||||
const api = {
|
||||
uploadUrl: `${getEnvBaseUrl()}/sys/common/upload`,
|
||||
}
|
||||
let stopWatch
|
||||
|
||||
const imgSrc = computed(() => {
|
||||
return props.modelValue
|
||||
})
|
||||
const handleDel = () => {
|
||||
message
|
||||
.confirm({
|
||||
msg: '确定要删除这个头像吗?',
|
||||
title: '提示',
|
||||
})
|
||||
.then(() => {
|
||||
emit('update:modelValue', '')
|
||||
})
|
||||
.catch(() => {
|
||||
console.log('点击了取消按钮')
|
||||
})
|
||||
}
|
||||
const handleUpload = () => {
|
||||
const { loading, data, error, run } = useUpload({ name: 'file' }, { url: api.uploadUrl })
|
||||
if (stopWatch) stopWatch()
|
||||
run()
|
||||
stopWatch = watch(
|
||||
() => [loading.value, error.value, data.value],
|
||||
([loading, err, data], oldValue) => {
|
||||
if (loading == false) {
|
||||
if (err) {
|
||||
toast.warning('修改失败')
|
||||
} else {
|
||||
if (data) {
|
||||
emit('update:modelValue', data.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.showArea {
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
.iconBox {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
border-bottom-left-radius: 3px;
|
||||
padding: 3px 6px;
|
||||
height: auto;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
.uploadArea {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
.iconBox {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: 2px solid #eee;
|
||||
color: #8799a3;
|
||||
margin: auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
font-size: 27px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
188
src/pages-user/userEdit/userEdit.vue
Normal file
188
src/pages-user/userEdit/userEdit.vue
Normal file
@ -0,0 +1,188 @@
|
||||
<route lang="json5" type="page">
|
||||
{
|
||||
layout: 'default',
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<PageLayout navTitle="编辑资料" backRouteName="people" routeMethod="pushTab">
|
||||
<wd-form custom-class="pt3" ref="form" :model="model">
|
||||
<wd-cell-group border>
|
||||
<wd-input
|
||||
label="用户名"
|
||||
prop="username"
|
||||
clearable
|
||||
label-width="70px"
|
||||
v-model="model.username"
|
||||
:readonly="true"
|
||||
placeholder="请输入用户名"
|
||||
/>
|
||||
<wd-input
|
||||
label="名称"
|
||||
prop="realname"
|
||||
clearable
|
||||
label-width="70px"
|
||||
v-model="model.realname"
|
||||
placeholder="请输入用户名"
|
||||
:rules="[{ validator: rules.realname }]"
|
||||
/>
|
||||
<wd-cell title="头像">
|
||||
<avatar v-model="model.avatar"></avatar>
|
||||
</wd-cell>
|
||||
<wd-select-picker
|
||||
label="性别"
|
||||
type="radio"
|
||||
v-model="model.sex"
|
||||
:columns="columns"
|
||||
title="请选择性别"
|
||||
:safe-area-inset-bottom="false"
|
||||
></wd-select-picker>
|
||||
<wd-input
|
||||
label="手机号"
|
||||
prop="phone"
|
||||
clearable
|
||||
label-width="70px"
|
||||
v-model="model.phone"
|
||||
:readonly="true"
|
||||
placeholder="请输入手机号"
|
||||
:rules="[{ validator: rules.phone }]"
|
||||
/>
|
||||
<wd-input
|
||||
label="邮箱"
|
||||
prop="email"
|
||||
clearable
|
||||
label-width="70px"
|
||||
v-model="model.email"
|
||||
:readonly="true"
|
||||
placeholder="请输入邮箱"
|
||||
:rules="[{ validator: rules.email }]"
|
||||
/>
|
||||
</wd-cell-group>
|
||||
<view class="footer p5">
|
||||
<wd-button type="primary" size="large" @click="handleSubmit" block>提交</wd-button>
|
||||
</view>
|
||||
</wd-form>
|
||||
</PageLayout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { http } from '@/utils/http'
|
||||
import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
|
||||
import { useRouter } from '@/plugin/uni-mini-router'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import avatar from './components/avatar.vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'chatList',
|
||||
options: {
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const toast = useToast()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const columns = [
|
||||
{ label: '男', value: 1 },
|
||||
{ label: '女', value: 2 },
|
||||
]
|
||||
const model = reactive({
|
||||
username: userStore.userInfo.username,
|
||||
realname: userStore.userInfo.realname,
|
||||
avatar: userStore.userInfo.avatar,
|
||||
sex: userStore.userInfo.sex ?? 1,
|
||||
phone: userStore.userInfo.phone,
|
||||
email: userStore.userInfo.email,
|
||||
})
|
||||
const rules = {
|
||||
realname: (value) => {
|
||||
if (value?.trim()?.length) {
|
||||
return true
|
||||
} else {
|
||||
toast.warning('请输入名称')
|
||||
return false
|
||||
}
|
||||
},
|
||||
phone: (value) => {
|
||||
if (value?.trim()?.length) {
|
||||
if (/^1[3-9]\d{9}$/.test(value)) {
|
||||
return true
|
||||
} else {
|
||||
toast.warning('请输入正确的手机号')
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
toast.warning('请输入手机号')
|
||||
return false
|
||||
}
|
||||
},
|
||||
email: (value) => {
|
||||
if (value?.trim()?.length) {
|
||||
if (/^[a-zA-Z0-9_%+-]+(\.[a-zA-Z0-9_%+-]+)*@([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/.test(value)) {
|
||||
return true
|
||||
} else {
|
||||
toast.warning('请输入正确的邮箱')
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
toast.warning('请输入邮箱')
|
||||
return false
|
||||
}
|
||||
},
|
||||
}
|
||||
const form = ref()
|
||||
|
||||
function handleSubmit() {
|
||||
form.value
|
||||
.validate()
|
||||
.then(({ valid, errors }) => {
|
||||
if (valid) {
|
||||
if (!model.avatar) {
|
||||
toast.warning('上传头像')
|
||||
return
|
||||
}
|
||||
// toast.success('校验通过')
|
||||
const data = { ...model, id: userStore.userInfo.userid }
|
||||
delete data.username
|
||||
uni.showLoading()
|
||||
http
|
||||
.post('/sys/user/login/setting/userEdit', { ...data })
|
||||
.then((res: any) => {
|
||||
uni.hideLoading()
|
||||
if (res.success) {
|
||||
toast.success('修改成功~')
|
||||
setTimeout(() => {
|
||||
userStore.editUserInfo({ ...data })
|
||||
router.replaceAll({ name: 'people' })
|
||||
}, 1e3)
|
||||
} else {
|
||||
toast.warning(res.message)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error('提交失败')
|
||||
uni.hideLoading()
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error, 'error')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
//
|
||||
:deep(.wd-cell) {
|
||||
.wd-cell__left {
|
||||
width: 70px;
|
||||
flex: none;
|
||||
}
|
||||
.wd-cell__value {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
28
src/pages-work/components/Empty.vue
Normal file
28
src/pages-work/components/Empty.vue
Normal file
@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<view class="empty-container">
|
||||
<image class="img" src="/static/nocontent-1.png"></image>
|
||||
<view class="text-center text-bold">
|
||||
{{title}}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
title:{
|
||||
type:String,
|
||||
default:"暂无数据"
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.empty-container{
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.img {max-width: 240upx;max-height: 240upx;}
|
||||
}
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user