初始化产品库
							
								
								
									
										25
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,25 @@ | ||||
| .DS_Store | ||||
| node_modules/ | ||||
| unpackage/ | ||||
| dist/ | ||||
|  | ||||
| # local env files | ||||
| .env.local | ||||
| .env.*.local | ||||
|  | ||||
| # Log files | ||||
| npm-debug.log* | ||||
| yarn-debug.log* | ||||
| yarn-error.log* | ||||
|  | ||||
| # Editor directories and files | ||||
| .project | ||||
| .idea | ||||
| .vscode | ||||
| *.suo | ||||
| *.ntvs* | ||||
| *.njsproj | ||||
| *.sln | ||||
| *.sw* | ||||
| /package-lock.json | ||||
| /.hbuilderx/launch.json | ||||
							
								
								
									
										64
									
								
								babel.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,64 @@ | ||||
| const plugins = [] | ||||
|  | ||||
| if (process.env.UNI_OPT_TREESHAKINGNG) { | ||||
|   plugins.push(require('@dcloudio/vue-cli-plugin-uni-optimize/packages/babel-plugin-uni-api/index.js')) | ||||
| } | ||||
|  | ||||
| if ( | ||||
|     ( | ||||
|         process.env.UNI_PLATFORM === 'app-plus' && | ||||
|         process.env.UNI_USING_V8 | ||||
|     ) || | ||||
|     ( | ||||
|         process.env.UNI_PLATFORM === 'h5' && | ||||
|         process.env.UNI_H5_BROWSER === 'builtin' | ||||
|     ) | ||||
| ) { | ||||
|   const path = require('path') | ||||
|  | ||||
|   const isWin = /^win/.test(process.platform) | ||||
|  | ||||
|   const normalizePath = path => (isWin ? path.replace(/\\/g, '/') : path) | ||||
|  | ||||
|   const input = normalizePath(process.env.UNI_INPUT_DIR) | ||||
|   try { | ||||
|     plugins.push([ | ||||
|       require('@dcloudio/vue-cli-plugin-hbuilderx/packages/babel-plugin-console'), | ||||
|       { | ||||
|         file(file) { | ||||
|           file = normalizePath(file) | ||||
|           if (file.indexOf(input) === 0) { | ||||
|             return path.relative(input, file) | ||||
|           } | ||||
|           return false | ||||
|         } | ||||
|       } | ||||
|     ]) | ||||
|   } catch (e) { | ||||
|   } | ||||
| } | ||||
|  | ||||
| process.UNI_LIBRARIES = process.UNI_LIBRARIES || ['@dcloudio/uni-ui'] | ||||
| process.UNI_LIBRARIES.forEach(libraryName => { | ||||
|   plugins.push([ | ||||
|     'import', | ||||
|     { | ||||
|       'libraryName': libraryName, | ||||
|       'customName': (name) => { | ||||
|         return `${libraryName}/lib/${name}/${name}` | ||||
|       } | ||||
|     } | ||||
|   ]) | ||||
| }) | ||||
| module.exports = { | ||||
|   presets: [ | ||||
|     [ | ||||
|       '@vue/app', | ||||
|       { | ||||
|         modules: 'commonjs', | ||||
|         useBuiltIns: process.env.UNI_PLATFORM === 'h5' ? 'usage' : 'entry' | ||||
|       } | ||||
|     ] | ||||
|   ], | ||||
|   plugins | ||||
| } | ||||
							
								
								
									
										84
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,84 @@ | ||||
| { | ||||
|   "name": "dv_cp_wechat", | ||||
|   "version": "0.1.0", | ||||
|   "private": true, | ||||
|   "scripts": { | ||||
|     "dev": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service uni-serve --minimize", | ||||
|     "build": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build", | ||||
|     "build:mp-weixin": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build", | ||||
|     "dev:mp-weixin": "cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch", | ||||
|     "info": "node node_modules/@dcloudio/vue-cli-plugin-uni/commands/info.js", | ||||
|     "test:mp-weixin": "cross-env UNI_PLATFORM=mp-weixin jest -i" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@amap/amap-jsapi-loader": "^1.0.1", | ||||
|     "@dcloudio/uni-app-plus": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/uni-h5": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/uni-helper-json": "*", | ||||
|     "@dcloudio/uni-mp-360": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/uni-mp-alipay": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/uni-mp-baidu": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/uni-mp-kuaishou": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/uni-mp-qq": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/uni-mp-toutiao": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/uni-mp-vue": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/uni-mp-weixin": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/uni-quickapp-native": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/uni-quickapp-webview": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/uni-stat": "^2.0.0-31820210406002", | ||||
|     "@vue/shared": "^3.0.0", | ||||
|     "axios": "^0.21.1", | ||||
|     "core-js": "^3.11.0", | ||||
|     "dayjs": "^1.10.6", | ||||
|     "echarts": "^4.9.0", | ||||
|     "recorder-core": "^1.1.21080800", | ||||
|     "regenerator-runtime": "^0.12.1", | ||||
|     "vue": "^2.6.11", | ||||
|     "vuedraggable": "^2.24.3", | ||||
|     "vuex": "^3.2.0", | ||||
|     "vuex-persistedstate": "^4.0.0-beta.3" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@babel/runtime": "~7.12.0", | ||||
|     "@dcloudio/types": "^2.2.1", | ||||
|     "@dcloudio/uni-automator": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/uni-cli-i18n": "^2.0.0-32920211029001", | ||||
|     "@dcloudio/uni-cli-shared": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/uni-i18n": "^2.0.0-32920211029001", | ||||
|     "@dcloudio/uni-migration": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/uni-template-compiler": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/vue-cli-plugin-hbuilderx": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/vue-cli-plugin-uni": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/vue-cli-plugin-uni-optimize": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/webpack-uni-mp-loader": "^2.0.0-31820210406002", | ||||
|     "@dcloudio/webpack-uni-pages-loader": "^2.0.0-31820210406002", | ||||
|     "@vue/cli-plugin-babel": "^4.5.12", | ||||
|     "@vue/cli-service": "~4.5.0", | ||||
|     "babel-plugin-import": "^1.11.0", | ||||
|     "cross-env": "^7.0.2", | ||||
|     "jest": "^25.4.0", | ||||
|     "mini-types": "*", | ||||
|     "miniprogram-api-typings": "^3.3.2", | ||||
|     "node-sass": "npm:dart-sass@^1.25.0", | ||||
|     "postcss-comment": "^2.0.0", | ||||
|     "sass-loader": "^7.1.0", | ||||
|     "vue-template-compiler": "^2.6.11" | ||||
|   }, | ||||
|   "browserslist": [ | ||||
|     "Android >= 4", | ||||
|     "ios >= 8" | ||||
|   ], | ||||
|   "uni-app": { | ||||
|     "scripts": { | ||||
|       "cp-weixin": { | ||||
|         "title": "企业微信端", | ||||
|         "env": { | ||||
|           "UNI_PLATFORM": "mp-weixin" | ||||
|         }, | ||||
|         "define": { | ||||
|           "CP-WEIXIN": true | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										22
									
								
								postcss.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,22 @@ | ||||
| const path = require('path') | ||||
| module.exports = { | ||||
|   parser: require('postcss-comment'), | ||||
|   plugins: [ | ||||
|     require('postcss-import')({ | ||||
|       resolve (id, basedir, importOptions) { | ||||
|         if (id.startsWith('~@/')) { | ||||
|           return path.resolve(process.env.UNI_INPUT_DIR, id.substr(3)) | ||||
|         } else if (id.startsWith('@/')) { | ||||
|           return path.resolve(process.env.UNI_INPUT_DIR, id.substr(2)) | ||||
|         } else if (id.startsWith('/') && !id.startsWith('//')) { | ||||
|           return path.resolve(process.env.UNI_INPUT_DIR, id.substr(1)) | ||||
|         } | ||||
|         return id | ||||
|       } | ||||
|     }), | ||||
|     require('autoprefixer')({ | ||||
|       remove: process.env.UNI_PLATFORM !== 'h5' | ||||
|     }), | ||||
|     require('@dcloudio/vue-cli-plugin-uni/packages/postcss') | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										1
									
								
								public/WW_verify_BeLokK3oa5SpR9ER.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| BeLokK3oa5SpR9ER | ||||
							
								
								
									
										1
									
								
								public/WW_verify_mSayjTrg7ZGXGC2D.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| mSayjTrg7ZGXGC2D | ||||
							
								
								
									
										1
									
								
								public/WW_verify_ual6jgcTCMb4cdLb.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| ual6jgcTCMb4cdLb | ||||
							
								
								
									
										28
									
								
								public/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,28 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="zh-CN"> | ||||
|  | ||||
| <head> | ||||
|   <meta charset="utf-8"> | ||||
|   <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
|   <title> | ||||
|     <%= htmlWebpackPlugin.options.title %> | ||||
|   </title> | ||||
|   <script> | ||||
|     document.addEventListener('DOMContentLoaded', function () { | ||||
|       document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px' | ||||
|     }) | ||||
|     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> | ||||
|   <link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css"/> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
| <noscript> | ||||
|   <strong>Please enable JavaScript to continue.</strong> | ||||
| </noscript> | ||||
| <div id="app"></div> | ||||
| <!-- built files will be auto injected --> | ||||
| </body> | ||||
|  | ||||
| </html> | ||||
							
								
								
									
										164
									
								
								src/App.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,164 @@ | ||||
| <script> | ||||
| import {mapMutations, mapState} from 'vuex' | ||||
|  | ||||
| export default { | ||||
|   provide() { | ||||
|     return { | ||||
|       root: this, | ||||
|     } | ||||
|   }, | ||||
|   onLaunch: function () { | ||||
|     this.initConfig() | ||||
|   }, | ||||
|   onShow: function () { | ||||
|     this.initWaterMarker() | ||||
|   }, | ||||
|   onPageNotFound() { | ||||
|     this.logout() | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['token', 'user']), | ||||
|   }, | ||||
|   methods: { | ||||
|     ...mapMutations(['initWaterMarker', 'logout', 'getConfig']), | ||||
|     initDev() { | ||||
|       let baseURL = 'http://192.168.1.87:9000' | ||||
|       this.getConfig({baseURL}) | ||||
|        | ||||
|       // this.$store.commit('login', 'bearer 88dd207a-dfe3-4f81-b9bd-e379de427d0b') | ||||
|     }, | ||||
|     initConfig() { | ||||
|       if (process.env.NODE_ENV == 'development') this.initDev() | ||||
|       else { | ||||
|         let baseURL = location.origin | ||||
|         this.getConfig({baseURL}) | ||||
|       } | ||||
|     }, | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss"> | ||||
| @import 'uview/index.scss'; | ||||
| @import './common/iconfont.css'; | ||||
|  | ||||
| body { | ||||
|   font-family: 'Microsoft YaHei', serif; | ||||
| } | ||||
|  | ||||
| uni-page-body { | ||||
|   min-height: 100%; | ||||
|   background: #f5f5f5; | ||||
|   position: relative; | ||||
| } | ||||
|  | ||||
| div[bottom] { | ||||
|   position: fixed; | ||||
|   left: 0; | ||||
|   bottom: 0; | ||||
|   width: 100%; | ||||
|   height: 128px; | ||||
|   padding: 24px 32px; | ||||
|   box-sizing: border-box; | ||||
|   background: #ffffff; | ||||
|   border-top: 1px solid #d4d4d4; | ||||
|   display: flex; | ||||
|   gap: 32px; | ||||
|  | ||||
|   & > * { | ||||
|     flex: 1; | ||||
|     min-width: 0; | ||||
|  | ||||
|     & + * { | ||||
|       margin-left: 32px; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| div[flex] { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|  | ||||
|   &.spb { | ||||
|     justify-content: space-between; | ||||
|   } | ||||
|  | ||||
|   &.wrap { | ||||
|     flex-wrap: wrap; | ||||
|   } | ||||
|  | ||||
|   &.column { | ||||
|     flex-direction: column; | ||||
|   } | ||||
|  | ||||
|   &.start { | ||||
|     align-items: flex-start; | ||||
|   } | ||||
| } | ||||
|  | ||||
| div[shrink] { | ||||
|   flex-shrink: 0; | ||||
| } | ||||
|  | ||||
| uni-button { | ||||
|   border-radius: 4px; | ||||
|  | ||||
|   &.u-btn--primary { | ||||
|     background-color: $uni-color-primary !important; | ||||
|     border-color: $uni-color-primary !important; | ||||
|   } | ||||
|  | ||||
|   &:after { | ||||
|     border-radius: 4px; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .fill { | ||||
|   flex: 1; | ||||
|   min-width: 0; | ||||
|   min-height: 0; | ||||
| } | ||||
|  | ||||
| .u-form-item { | ||||
|   width: 100%; | ||||
|   min-height: 100px; | ||||
|   background: #ffffff; | ||||
|   padding: 0 32px !important; | ||||
|   box-sizing: border-box; | ||||
|   display: flex; | ||||
|   line-height: normal !important; | ||||
|  | ||||
|   .u-form-item__body { | ||||
|     height: inherit; | ||||
|     flex: 1; | ||||
|  | ||||
|     .u-form-item--left { | ||||
|       min-height: 100px; | ||||
|     } | ||||
|  | ||||
|     .u-form-item--right__content__slot { | ||||
|       padding-bottom: 10px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .u-form-item__message { | ||||
|     margin: 15px 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @keyframes mapWarn { | ||||
|   0% { | ||||
|     transform: scale(.5); | ||||
|     opacity: 1 | ||||
|   } | ||||
|  | ||||
|   30% { | ||||
|     opacity: .5 | ||||
|   } | ||||
|  | ||||
|   to { | ||||
|     transform: scale(1.4); | ||||
|     opacity: 0 | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										61
									
								
								src/common/axios.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,61 @@ | ||||
| import axios from 'axios' | ||||
| import store from '../store' | ||||
| import util from "./util"; | ||||
|  | ||||
| let instance = axios.create({ | ||||
|   timeout: 600000, | ||||
|   withCredentials: true, | ||||
| }) | ||||
| instance.interceptors.request.use(config => { | ||||
|   store.commit('initWaterMarker') | ||||
|   if (!config.withoutToken && store.state.token) { | ||||
|     config.headers["Authorization"] = store.state.token | ||||
|   } | ||||
|   return config | ||||
| }, err => { | ||||
|   console.error(err) | ||||
| }) | ||||
|  | ||||
| instance.interceptors.response.use(res => { | ||||
|   if (res.data) { | ||||
|     if (res.data.code) { | ||||
|       if (res.data.code == 0) { | ||||
|         return res.data | ||||
|       } else if (res.data.code == 401) { | ||||
|         store.commit("logout"); | ||||
|         let reg = new RegExp('.*code=(.+$)', "g") | ||||
|         if (reg.test(location.search)) { | ||||
|           let code = location.search.replace(reg, '$1') | ||||
|           store.commit('bindAccount', { | ||||
|             code, then: res => { | ||||
|               store.commit("login", [res?.token_type, res?.access_token].join(" ").trim()) | ||||
|               location.href = location.href.replace('code=' + code, '') | ||||
|             } | ||||
|           }) | ||||
|         } else util.confirm("用户信息验证失效,是否要重新登录?").then(() => { | ||||
|           store.commit('redirectCode') | ||||
|           // let app = store.state.apps?.find(e => location.href.indexOf(e.path) > -1) | ||||
|           // const goto = path => { | ||||
|           //   location.href = location.origin + "/pages/loading?" + path | ||||
|           // } | ||||
|           // if (app) { | ||||
|           //   goto(location.search + `&app=${app.key}`) | ||||
|           // } else { | ||||
|           //   goto(location.search + `#error`) | ||||
|           // } | ||||
|         }).catch(() => 0) | ||||
|       } else { | ||||
|         console.error(res.data.msg || "请求失败!") | ||||
|         return Promise.reject(res.data.msg) | ||||
|       } | ||||
|     } else { | ||||
|       return res.data | ||||
|     } | ||||
|   } else { | ||||
|     console.error("服务器异常,请联系管理员!") | ||||
|   } | ||||
| }, err => { | ||||
|   console.error(err) | ||||
| }) | ||||
|  | ||||
| export default instance | ||||
							
								
								
									
										50
									
								
								src/common/dict.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,50 @@ | ||||
| import request from "./axios"; | ||||
| import store from "../store"; | ||||
|  | ||||
| /** | ||||
|  * 封装字典工具类 | ||||
|  */ | ||||
| const $dict = { | ||||
|   vueStore: null, | ||||
|   url: "/admin/dictionary/queryValsByCodeList", | ||||
|   setUrl(v) { | ||||
|     this.url = v | ||||
|   }, | ||||
|   load(...code) { | ||||
|     if (!this.vueStore) this.vueStore = store | ||||
|     return request.post(this.url, null, { | ||||
|       params: { | ||||
|         codeList: code.join(',') | ||||
|       } | ||||
|     }).then((res) => { | ||||
|       this.vueStore.commit("setDicts", res.data) | ||||
|     }) | ||||
|   }, | ||||
|   getDict(key) { | ||||
|     let dict = this.vueStore.getters.getDict(key) | ||||
|     return dict ? dict.values : [] | ||||
|   }, | ||||
|   getValue(key, label) { | ||||
|     let dict = this.vueStore.getters.getDict(key) | ||||
|     if (dict) { | ||||
|       let item = dict.values.find(v => v.dictName == label) | ||||
|       return item ? item.dictValue : label | ||||
|     } else return label | ||||
|   }, | ||||
|   getLabel(key, value) { | ||||
|     let dict = this.vueStore.getters.getDict(key) | ||||
|     if (dict) { | ||||
|       let item = dict.values.find(v => v.dictValue == value) | ||||
|       return item ? item.dictName : value | ||||
|     } else return value | ||||
|   }, | ||||
|   getColor(key, value) { | ||||
|     let dict = this.vueStore.getters.getDict(key) | ||||
|     if (dict) { | ||||
|       let item = dict.values.find(v => v.dictValue == value) | ||||
|       return item ? item.dictColor : value | ||||
|     } else return value | ||||
|   }, | ||||
| } | ||||
|  | ||||
| export default $dict | ||||
							
								
								
									
										712
									
								
								src/common/iconfont.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										245
									
								
								src/common/permission.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,245 @@ | ||||
| /// null = 未请求,1 = 已允许,0 = 拒绝|受限, 2 = 系统未开启 | ||||
|  | ||||
| var isIOS | ||||
|  | ||||
| function album() { | ||||
|     var result = 0; | ||||
|     var PHPhotoLibrary = plus.ios.import("PHPhotoLibrary"); | ||||
|     var authStatus = PHPhotoLibrary.authorizationStatus(); | ||||
|     if (authStatus === 0) { | ||||
|         result = null; | ||||
|     } else if (authStatus == 3) { | ||||
|         result = 1; | ||||
|     } else { | ||||
|         result = 0; | ||||
|     } | ||||
|     plus.ios.deleteObject(PHPhotoLibrary); | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| function camera() { | ||||
|     var result = 0; | ||||
|     var AVCaptureDevice = plus.ios.import("AVCaptureDevice"); | ||||
|     var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide'); | ||||
|     if (authStatus === 0) { | ||||
|         result = null; | ||||
|     } else if (authStatus == 3) { | ||||
|         result = 1; | ||||
|     } else { | ||||
|         result = 0; | ||||
|     } | ||||
|     plus.ios.deleteObject(AVCaptureDevice); | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| function location() { | ||||
|     var result = 0; | ||||
|     var cllocationManger = plus.ios.import("CLLocationManager"); | ||||
|     var enable = cllocationManger.locationServicesEnabled(); | ||||
|     var status = cllocationManger.authorizationStatus(); | ||||
|     if (!enable) { | ||||
|         result = 2; | ||||
|     } else if (status === 0) { | ||||
|         result = null; | ||||
|     } else if (status === 3 || status === 4) { | ||||
|         result = 1; | ||||
|     } else { | ||||
|         result = 0; | ||||
|     } | ||||
|     plus.ios.deleteObject(cllocationManger); | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| function push() { | ||||
|     var result = 0; | ||||
|     var UIApplication = plus.ios.import("UIApplication"); | ||||
|     var app = UIApplication.sharedApplication(); | ||||
|     var enabledTypes = 0; | ||||
|     if (app.currentUserNotificationSettings) { | ||||
|         var settings = app.currentUserNotificationSettings(); | ||||
|         enabledTypes = settings.plusGetAttribute("types"); | ||||
|         if (enabledTypes == 0) { | ||||
|             result = 0; | ||||
|             console.log("推送权限没有开启"); | ||||
|         } else { | ||||
|             result = 1; | ||||
|             console.log("已经开启推送功能!") | ||||
|         } | ||||
|         plus.ios.deleteObject(settings); | ||||
|     } else { | ||||
|         enabledTypes = app.enabledRemoteNotificationTypes(); | ||||
|         if (enabledTypes == 0) { | ||||
|             result = 3; | ||||
|             console.log("推送权限没有开启!"); | ||||
|         } else { | ||||
|             result = 4; | ||||
|             console.log("已经开启推送功能!") | ||||
|         } | ||||
|     } | ||||
|     plus.ios.deleteObject(app); | ||||
|     plus.ios.deleteObject(UIApplication); | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| function contact() { | ||||
|     var result = 0; | ||||
|     var CNContactStore = plus.ios.import("CNContactStore"); | ||||
|     var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0); | ||||
|     if (cnAuthStatus === 0) { | ||||
|         result = null; | ||||
|     } else if (cnAuthStatus == 3) { | ||||
|         result = 1; | ||||
|     } else { | ||||
|         result = 0; | ||||
|     } | ||||
|     plus.ios.deleteObject(CNContactStore); | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| function record() { | ||||
|     var result = null; | ||||
|     var avaudiosession = plus.ios.import("AVAudioSession"); | ||||
|     var avaudio = avaudiosession.sharedInstance(); | ||||
|     var status = avaudio.recordPermission(); | ||||
|     console.log("permissionStatus:" + status); | ||||
|     if (status === 1970168948) { | ||||
|         result = null; | ||||
|     } else if (status === 1735552628) { | ||||
|         result = 1; | ||||
|     } else { | ||||
|         result = 0; | ||||
|     } | ||||
|     plus.ios.deleteObject(avaudiosession); | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| function calendar() { | ||||
|     var result = null; | ||||
|     var EKEventStore = plus.ios.import("EKEventStore"); | ||||
|     var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0); | ||||
|     if (ekAuthStatus == 3) { | ||||
|         result = 1; | ||||
|         console.log("日历权限已经开启"); | ||||
|     } else { | ||||
|         console.log("日历权限没有开启"); | ||||
|     } | ||||
|     plus.ios.deleteObject(EKEventStore); | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| function memo() { | ||||
|     var result = null; | ||||
|     var EKEventStore = plus.ios.import("EKEventStore"); | ||||
|     var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1); | ||||
|     if (ekAuthStatus == 3) { | ||||
|         result = 1; | ||||
|         console.log("备忘录权限已经开启"); | ||||
|     } else { | ||||
|         console.log("备忘录权限没有开启"); | ||||
|     } | ||||
|     plus.ios.deleteObject(EKEventStore); | ||||
|     return result; | ||||
| } | ||||
|  | ||||
|  | ||||
| function requestIOS(permissionID) { | ||||
|     return new Promise((resolve, reject) => { | ||||
|         switch (permissionID) { | ||||
|             case "push": | ||||
|                 resolve(push()); | ||||
|                 break; | ||||
|             case "location": | ||||
|                 resolve(location()); | ||||
|                 break; | ||||
|             case "record": | ||||
|                 resolve(record()); | ||||
|                 break; | ||||
|             case "camera": | ||||
|                 resolve(camera()); | ||||
|                 break; | ||||
|             case "album": | ||||
|                 resolve(album()); | ||||
|                 break; | ||||
|             case "contact": | ||||
|                 resolve(contact()); | ||||
|                 break; | ||||
|             case "calendar": | ||||
|                 resolve(calendar()); | ||||
|                 break; | ||||
|             case "memo": | ||||
|                 resolve(memo()); | ||||
|                 break; | ||||
|             default: | ||||
|                 resolve(0); | ||||
|                 break; | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function requestAndroid(permissionID) { | ||||
|     return new Promise((resolve, reject) => { | ||||
|         plus.android.requestPermissions( | ||||
|             [permissionID], | ||||
|             function(resultObj) { | ||||
|                 var result = 0; | ||||
|                 for (var i = 0; i < resultObj.granted.length; i++) { | ||||
|                     var grantedPermission = resultObj.granted[i]; | ||||
|                     console.log('已获取的权限:' + grantedPermission); | ||||
|                     result = 1 | ||||
|                 } | ||||
|                 for (var i = 0; i < resultObj.deniedPresent.length; i++) { | ||||
|                     var deniedPresentPermission = resultObj.deniedPresent[i]; | ||||
|                     console.log('拒绝本次申请的权限:' + deniedPresentPermission); | ||||
|                     result = 0 | ||||
|                 } | ||||
|                 for (var i = 0; i < resultObj.deniedAlways.length; i++) { | ||||
|                     var deniedAlwaysPermission = resultObj.deniedAlways[i]; | ||||
|                     console.log('永久拒绝申请的权限:' + deniedAlwaysPermission); | ||||
|                     result = -1 | ||||
|                 } | ||||
|                 resolve(result); | ||||
|             }, | ||||
|             function(error) { | ||||
|                 console.log('result error: ' + error.message) | ||||
|                 resolve({ | ||||
|                     code: error.code, | ||||
|                     message: error.message | ||||
|                 }); | ||||
|             } | ||||
|         ); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function gotoAppPermissionSetting() { | ||||
|     if (permission.isIOS) { | ||||
|         var UIApplication = plus.ios.import("UIApplication"); | ||||
|         var application2 = UIApplication.sharedApplication(); | ||||
|         var NSURL2 = plus.ios.import("NSURL"); | ||||
|         var setting2 = NSURL2.URLWithString("app-settings:"); | ||||
|         application2.openURL(setting2); | ||||
|         plus.ios.deleteObject(setting2); | ||||
|         plus.ios.deleteObject(NSURL2); | ||||
|         plus.ios.deleteObject(application2); | ||||
|     } else { | ||||
|         var Intent = plus.android.importClass("android.content.Intent"); | ||||
|         var Settings = plus.android.importClass("android.provider.Settings"); | ||||
|         var Uri = plus.android.importClass("android.net.Uri"); | ||||
|         var mainActivity = plus.android.runtimeMainActivity(); | ||||
|         var intent = new Intent(); | ||||
|         intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); | ||||
|         var uri = Uri.fromParts("package", mainActivity.getPackageName(), null); | ||||
|         intent.setData(uri); | ||||
|         mainActivity.startActivity(intent); | ||||
|     } | ||||
| } | ||||
|  | ||||
| const permission = { | ||||
|     get isIOS(){ | ||||
|         return typeof isIOS === 'boolean' ? isIOS : (isIOS = uni.getSystemInfoSync().platform === 'ios') | ||||
|     }, | ||||
|     requestIOS: requestIOS, | ||||
|     requestAndroid: requestAndroid, | ||||
|     gotoAppSetting: gotoAppPermissionSetting | ||||
| } | ||||
|  | ||||
| module.exports = permission | ||||
							
								
								
									
										59
									
								
								src/common/util.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,59 @@ | ||||
| import dict from "./dict"; | ||||
| import toast from '../uview/libs/function/toast' | ||||
| import addUnit from '../uview/libs/function/addUnit' | ||||
| import $parent from '../uview/libs/function/$parent' | ||||
| import guid from '../uview/libs/function/guid' | ||||
| import deepClone from '../uview/libs/function/deepClone' | ||||
| import debounce from '../uview/libs/function/debounce' | ||||
| import throttle from '../uview/libs/function/throttle' | ||||
| import trim from '../uview/libs/function/trim' | ||||
| import {sys} from '../uview/libs/function/sys' | ||||
| import test from '../uview/libs/function/test' | ||||
| import config from '../uview/libs/config/config' | ||||
| import zIndex from '../uview/libs/config/zIndex' | ||||
| import $moment from 'dayjs' | ||||
|  | ||||
| const confirm = (content, title, config) => { | ||||
|   let ops = {content} | ||||
|   if (typeof title == 'object') { | ||||
|     ops = {...ops, ...title} | ||||
|   } else ops = {...ops, title: title || "提示"} | ||||
|   return new Promise((resolve, reject) => { | ||||
|     uni.showModal({ | ||||
|       ...ops, ...config, success: res => { | ||||
|         if (res?.confirm) { | ||||
|           resolve() | ||||
|         } else if (res?.cancel) { | ||||
|           reject() | ||||
|         } | ||||
|       } | ||||
|     }) | ||||
|   }) | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 获取年龄 | ||||
|  * @param code | ||||
|  */ | ||||
| const calcAge = (code) => { | ||||
|   let birthday | ||||
|   if (typeof code == 'string' && code.length == 18) { | ||||
|     birthday = $moment(code.substring(6, 14), 'YYYYMMDD') | ||||
|   } else if (typeof code == 'object') { | ||||
|     birthday = code | ||||
|   } | ||||
|   return Math.ceil($moment().year() - $moment(birthday).year()) | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| const u = {toast, $parent, addUnit, guid, config, zIndex, deepClone, throttle, debounce, trim, test, sys} | ||||
| export default { | ||||
|   dict, | ||||
|   confirm, | ||||
|   calcAge, | ||||
|   u, | ||||
|   dateFormat: (time, format) => { | ||||
|     return $moment(time).format(format || 'YYYY-MM-DD').replace("Invalid Date", "") | ||||
|   } | ||||
| } | ||||
							
								
								
									
										69
									
								
								src/components/AiAdd.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,69 @@ | ||||
| <template> | ||||
|   <movable-area class="movableArea"> | ||||
|     <movable-view direction="all" x="300" y="500"> | ||||
|       <div class="AiAdd" @click.stop="add"></div> | ||||
|     </movable-view> | ||||
|   </movable-area> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   export default { | ||||
|     name: "AiAdd", | ||||
|     props: { | ||||
|  | ||||
|     }, | ||||
|     data() { | ||||
|       return {} | ||||
|     }, | ||||
|     methods: { | ||||
|       add() { | ||||
|         this.$emit("add") | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .movableArea { | ||||
|     position: fixed; | ||||
|     left: 0; | ||||
|     top: 0; | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     pointer-events: none; | ||||
|     z-index: 999; | ||||
|  | ||||
|     uni-movable-view { | ||||
|       pointer-events: auto; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .AiAdd { | ||||
|     width: 96px; | ||||
|     height: 96px; | ||||
|     background: #1365DD; | ||||
|     box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); | ||||
|     border-radius: 50%; | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     flex-direction: column; | ||||
|     justify-content: center; | ||||
|     &:before , &:after{ | ||||
|       content: ""; | ||||
|       background: #FFFFFF; | ||||
|       display: block; | ||||
|       position: absolute; | ||||
|       border-radius: 4px; | ||||
|     } | ||||
|  | ||||
|     &:before{ | ||||
|       height: 48px; | ||||
|       width: 4px; | ||||
|     } | ||||
|  | ||||
|     &:after{ | ||||
|       height: 4px; | ||||
|       width: 48px; | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
							
								
								
									
										87
									
								
								src/components/AiBack.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,87 @@ | ||||
| <template> | ||||
|   <ai-fixed-btn v-if="!isTopPage||custom"> | ||||
|     <div class="AiBack" @click.stop="back"> | ||||
|       <img :src="imgHomeUrl + 'back.png'" alt=""> | ||||
|       <text>返回</text> | ||||
|     </div> | ||||
|   </ai-fixed-btn> | ||||
| </template> | ||||
| <script> | ||||
| import AiFixedBtn from "./AiFixedBtn"; | ||||
|  | ||||
| export default { | ||||
|   name: "AiBack", | ||||
|   components: {AiFixedBtn}, | ||||
|   props: { | ||||
|     delta: { | ||||
|       type: Number, | ||||
|       default: 1 | ||||
|     }, | ||||
|     eventName: { | ||||
|       type: String, | ||||
|       default: '' | ||||
|     }, | ||||
|     data: { | ||||
|       type: Object | Boolean, | ||||
|       default: () => { | ||||
|       } | ||||
|     }, | ||||
|     custom: Boolean, | ||||
|     visible: Boolean, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       isTopPage: false | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     back() { | ||||
|       if (this.visible) | ||||
|         return this.$parent.$emit(this.eventName, this.data) | ||||
|  | ||||
|       if (this.custom) { | ||||
|         this.$emit("back") | ||||
|       } else uni.navigateBack({ | ||||
|         delta: this.delta, | ||||
|         success: () => { | ||||
|           if (this.eventName != '') { | ||||
|             uni.$emit(this.eventName, this.data) | ||||
|           } | ||||
|         }, | ||||
|         fail: (err) => { | ||||
|           console.error(err) | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.isTopPage = window.history.length <= 1 | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiBack { | ||||
|   width: 108px; | ||||
|   height: 108px; | ||||
|   background: #6BA1F9; | ||||
|   box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.12); | ||||
|   border-radius: 50%; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   flex-direction: column; | ||||
|   justify-content: center; | ||||
|  | ||||
|   img { | ||||
|     width: 40px; | ||||
|     height: 40px; | ||||
|   } | ||||
|  | ||||
|   text { | ||||
|     font-size: 26px; | ||||
|     font-weight: 800; | ||||
|     color: #FFFFFF; | ||||
|     line-height: 40px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										115
									
								
								src/components/AiCard.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,115 @@ | ||||
| <template> | ||||
|   <section class="AiCard"> | ||||
|     <div flex v-if="$slots.custom" class="start"> | ||||
|       <div class="fill"> | ||||
|         <slot name="custom"/> | ||||
|       </div> | ||||
|       <div v-if="$slots.menu" class="iconfont iconfont-iconMore" @tap.stop="handleMore"/> | ||||
|     </div> | ||||
|     <template v-else> | ||||
|       <u-row> | ||||
|         <div class="content"> | ||||
|           <slot/> | ||||
|         </div> | ||||
|         <div btn @tap="$emit('send')">发送</div> | ||||
|       </u-row> | ||||
|       <u-row justify="space-between"> | ||||
|         <slot v-if="$slots.title" name="title"/> | ||||
|         <div v-else>{{ cardTitle }}</div> | ||||
|         <div v-if="$slots.menu" class="iconfont iconfont-iconMore" @tap.stop="handleMore"/> | ||||
|       </u-row> | ||||
|     </template> | ||||
|     <div v-if="menu" class="mask" @click="menu=false"> | ||||
|       <div class="moreMenu" :style="menuPos"> | ||||
|         <slot name="menu"/> | ||||
|       </div> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "AiCard", | ||||
|   props: { | ||||
|     cardTitle: String | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       menuPos: {}, | ||||
|       menu: false | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     handleMore({detail}) { | ||||
|       this.menuPos = { | ||||
|         left: detail.x - 10 + 'px', | ||||
|         top: detail.y + 'px' | ||||
|       } | ||||
|       this.menu = !this.menu | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiCard { | ||||
|   width: 100%; | ||||
|   background: #FFFFFF; | ||||
|   box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.02); | ||||
|   border-radius: 8px; | ||||
|   padding: 20px; | ||||
|   box-sizing: border-box; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   color: #999; | ||||
|   font-size: 26px; | ||||
|   flex-shrink: 0; | ||||
|  | ||||
|   .content { | ||||
|     background: #F9F9F9; | ||||
|     border-radius: 4px; | ||||
|     min-height: 120px; | ||||
|     flex: 1; | ||||
|     min-width: 0; | ||||
|     margin-bottom: 26px; | ||||
|     padding: 20px; | ||||
|     box-sizing: border-box; | ||||
|     color: #333; | ||||
|   } | ||||
|  | ||||
|   .u-row { | ||||
|     flex-wrap: nowrap; | ||||
|   } | ||||
|  | ||||
|   div[btn] { | ||||
|     color: #1365DD; | ||||
|     padding: 0 0 0 18px; | ||||
|     cursor: pointer; | ||||
|   } | ||||
|  | ||||
|   .iconfont-iconMore { | ||||
|     font-size: 38px; | ||||
|     transform: rotate(90deg); | ||||
|   } | ||||
|  | ||||
|   .moreMenu { | ||||
|     position: fixed; | ||||
|     background: #FFFFFF; | ||||
|     box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.1); | ||||
|     border-radius: 4px; | ||||
|     transform: translate(-100%, -100%); | ||||
|     min-width: 100px; | ||||
|     min-height: 100px; | ||||
|     z-index: 9; | ||||
|   } | ||||
|  | ||||
|   .mask { | ||||
|     position: fixed; | ||||
|     top: 0; | ||||
|     bottom: 0; | ||||
|     left: 0; | ||||
|     right: 0; | ||||
|     z-index: 11; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										76
									
								
								src/components/AiCell.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,76 @@ | ||||
| <template> | ||||
|   <section class="AiCell" :class="{bottomBorder,alignCenter,topLabel}"> | ||||
|     <div class="label" :class="{title}"> | ||||
|       <slot v-if="$slots.label" name="label"/> | ||||
|       <span v-else>{{ label }}</span> | ||||
|     </div> | ||||
|     <div class="content" :class="{topLabel}"> | ||||
|       <slot/> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "AiCell", | ||||
|   props: { | ||||
|     label: {default: ""}, | ||||
|     bottomBorder: Boolean, | ||||
|     topLabel: Boolean, | ||||
|     title: Boolean, | ||||
|     alignCenter: Boolean | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiCell { | ||||
|   display: flex; | ||||
|   min-height: 72px; | ||||
|   font-size: 30px; | ||||
|   color: #333; | ||||
|   padding: 14px 0; | ||||
|   box-sizing: border-box; | ||||
|   justify-content: space-between; | ||||
|  | ||||
|   &.bottomBorder { | ||||
|     border-bottom: 1px solid #eee; | ||||
|   } | ||||
|  | ||||
|   &.alignCenter { | ||||
|     align-items: center; | ||||
|   } | ||||
|  | ||||
|   &.topLabel { | ||||
|     flex-direction: column; | ||||
|   } | ||||
|  | ||||
|   .label { | ||||
|     min-width: 60px; | ||||
|     flex-shrink: 0; | ||||
|     width: auto; | ||||
|     color: #999; | ||||
|  | ||||
|  | ||||
|     &.title { | ||||
|       color: #333; | ||||
|       font-weight: bold; | ||||
|       font-size: 34px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .content { | ||||
|     flex: 1; | ||||
|     min-width: 100px; | ||||
|     min-height: 40px; | ||||
|     max-width: 500px; | ||||
|     text-align: right; | ||||
|  | ||||
|     &.topLabel { | ||||
|       text-align: start; | ||||
|       margin-top: 16px; | ||||
|       max-width: 100%; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										60
									
								
								src/components/AiDate.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,60 @@ | ||||
| <template> | ||||
|   <section class="AiDate"> | ||||
|     <u-calendar v-model="show" @change="handleSelect" :mode="mode"/> | ||||
|     <div flex @click="show=true"> | ||||
|       <div v-if="label" v-html="label"/> | ||||
|       <div v-else v-html="placeholder"/> | ||||
|       <i class="iconfont iconfont-iconArrow_Down"/> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import UCalendar from "../uview/components/u-calendar/u-calendar"; | ||||
| import dayjs from 'dayjs' | ||||
|  | ||||
| export default { | ||||
|   name: "AiDate", | ||||
|   components: {UCalendar}, | ||||
|   computed: { | ||||
|     label() { | ||||
|       let arr = (this.selected || this.value)?.toString()?.split(",") || [] | ||||
|       arr = arr.map(e => dayjs(e).format("MM-DD").replace("Invalid Date", '')) | ||||
|       return arr.join('至') | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       show: false, | ||||
|       selected: "" | ||||
|     } | ||||
|   }, | ||||
|   props: { | ||||
|     value: {default: ""}, | ||||
|     placeholder: {default: "请选择"}, | ||||
|     mode: {default: "date"},//date 单个日期|range 日期范围 | ||||
|   }, | ||||
|   methods: { | ||||
|     handleSelect(v) { | ||||
|       if (this.mode == 'date') { | ||||
|         this.selected = v.result | ||||
|         this.$emit('change', v.result) | ||||
|       } else if (this.mode == 'range') { | ||||
|         this.selected = [v.startDate, v.endDate] | ||||
|         this.$emit('change', v) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiDate { | ||||
|   color: #333333; | ||||
|  | ||||
|   .iconfont-iconArrow_Down { | ||||
|     margin-left: 4px; | ||||
|     font-size: 32px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										39
									
								
								src/components/AiEmpty/AiEmpty.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,39 @@ | ||||
| <template> | ||||
|     <div class="emptyWrap"> | ||||
|         <img class="emptyImg" src="./static/Empty.png"> | ||||
|         <div class="emptyText">{{description}}</div> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   export default { | ||||
|     name:"emptyData", | ||||
|     props:{ | ||||
|       description:{ | ||||
|         default:'暂无相关信息', | ||||
|         type:String | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|     .emptyWrap { | ||||
|         width: 100%; | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         .emptyImg{ | ||||
|             width: 400rpx; | ||||
|             height: 240rpx; | ||||
|             margin-top: 112px; | ||||
|         } | ||||
|         .emptyText{ | ||||
|             font-size:29rpx; | ||||
|             font-family:PingFangSC-Regular,PingFang SC; | ||||
|             font-weight:400; | ||||
|             color:rgba(183,183,183,1); | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
							
								
								
									
										
											BIN
										
									
								
								src/components/AiEmpty/static/1/Icon/Fields/bianhao@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 806 B | 
							
								
								
									
										
											BIN
										
									
								
								src/components/AiEmpty/static/1/Icon/Fields/time@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/components/AiEmpty/static/Empty.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.1 KiB | 
							
								
								
									
										36
									
								
								src/components/AiFixedBtn.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,36 @@ | ||||
| <template> | ||||
|   <section class="AiFixedBtn"> | ||||
|     <movable-area class="movableArea"> | ||||
|       <movable-view direction="all" x="300" y="500" @tap.stop> | ||||
|         <slot/> | ||||
|       </movable-view> | ||||
|     </movable-area> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "AiFixedBtn" | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiFixedBtn { | ||||
| } | ||||
|  | ||||
| .movableArea { | ||||
|   position: fixed; | ||||
|   left: 0; | ||||
|   top: 0; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   pointer-events: none; | ||||
|   z-index: 999; | ||||
|  | ||||
|   uni-movable-view { | ||||
|     pointer-events: auto; | ||||
|     width: auto; | ||||
|     height: auto; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										72
									
								
								src/components/AiImage.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,72 @@ | ||||
| <template> | ||||
|   <section class="AiImage"> | ||||
|     <div v-if="$slots.default" @tap="prev"> | ||||
|       <slot/> | ||||
|     </div> | ||||
|     <u-image v-else :src="src" @tap="prev"> | ||||
|       <image v-if="link" class="errorImage" slot="error" :src="$cdn+'link.png'"/> | ||||
|       <image v-else-if="miniapp" class="errorImage" slot="error" :src="$cdn+'miniwxmp.jpg'"/> | ||||
|       <image v-else class="errorImage" slot="error" :src="$cdn+'file.png'"/> | ||||
|     </u-image> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import UImage from "../uview/components/u-image/u-image"; | ||||
| import UModal from "../uview/components/u-modal/u-modal"; | ||||
| import {mapActions} from "vuex"; | ||||
|  | ||||
| export default { | ||||
|   name: "AiImage", | ||||
|   components: {UModal, UImage}, | ||||
|   data() { | ||||
|     return { | ||||
|       dialog: false | ||||
|     } | ||||
|   }, | ||||
|   props: { | ||||
|     src: String, | ||||
|     preview: Boolean, | ||||
|     link: Boolean, | ||||
|     miniapp: Boolean, | ||||
|     file: { | ||||
|       default: () => { | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     ...mapActions(['previewFile', 'injectJWeixin']), | ||||
|     prev() { | ||||
|       if (this.preview) { | ||||
|         if (!!this.src) { | ||||
|           uni.previewImage({ | ||||
|             current: this.src, | ||||
|             urls: [this.src] | ||||
|           }) | ||||
|         } else { | ||||
|           this.previewFile({size: 1, ...this.file}) | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiImage { | ||||
|   ::v-deep image { | ||||
|     width: 160px; | ||||
|     height: 160px; | ||||
|     object-fit: cover; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .u-image__error { | ||||
|     position: relative; | ||||
|   } | ||||
|  | ||||
|   .errorImage { | ||||
|     width: 80px; | ||||
|     height: 80px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										28
									
								
								src/components/AiLoading.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,28 @@ | ||||
| <template> | ||||
|   <section class="AiLoading"> | ||||
|     <image :src="image"/> | ||||
|     <span>{{ tips }}</span> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "AiLoading", | ||||
|   props: { | ||||
|     tips: {default: "应用加载中"}, | ||||
|     image: {default: "https://cdn.cunwuyun.cn/wxAdmin/img/message.png"} | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiLoading { | ||||
|   font-size: 32px; | ||||
|   color: #666; | ||||
|   text-align: center; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										68
									
								
								src/components/AiMap.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,68 @@ | ||||
| <template> | ||||
|   <section class="AiMap"> | ||||
|     <div ref="amap" class="map"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import AMapLoader from "@amap/amap-jsapi-loader"; | ||||
|  | ||||
| export default { | ||||
|   name: "AiMap", | ||||
|   props: { | ||||
|     plugins: {default: () => ['AMap.DistrictSearch']}, | ||||
|     map: Object, | ||||
|     lib: Object | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       amap: null | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     initMap() { | ||||
|       let {plugins} = this | ||||
|       AMapLoader.load({ | ||||
|         key: '54a02a43d9828a8f9cd4f26fe281e74e', | ||||
|         version: '2.0', | ||||
|         plugins | ||||
|       }).then(AMap => { | ||||
|         this.amap = new AMap.Map(this.$refs.amap, { | ||||
|           resizeEnable: true, | ||||
|           zoom: 14, | ||||
|         }) | ||||
|         this.$emit('update:lib', AMap) | ||||
|         this.$emit('update:map', this.amap) | ||||
|       }) | ||||
|     }, | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.initMap() | ||||
|   }, | ||||
|   destroyed() { | ||||
|     this.amap?.destroy() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiMap { | ||||
|   .map { | ||||
|     height: 100%; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .amap-logo, ::v-deep .amap-copyright { | ||||
|     display: none !important; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .amap-icon { | ||||
|     width: 40px !important; | ||||
|     height: 40px !important; | ||||
|  | ||||
|     img { | ||||
|       width: 100%; | ||||
|       height: 100%; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										85
									
								
								src/components/AiResult.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,85 @@ | ||||
| <template> | ||||
|   <section class="AiResult"> | ||||
|     <slot v-if="$slots.default"/> | ||||
|     <template v-else> | ||||
|       <image :src="result.image"/> | ||||
|       <span class="tips">{{ result.tips }}</span> | ||||
|       <slot name="extra" class="extra" v-if="$slots.extra"/> | ||||
|       <div v-if="result.btn" class="btn" @tap="handleTap">{{ result.btn }}</div> | ||||
|     </template> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "AiResult", | ||||
|   props: { | ||||
|     tips: {default: "提交成功!"}, | ||||
|     image: {default: "https://cdn.cunwuyun.cn/dvcp/h5/result/success.png"}, | ||||
|     btn: {default: ""}, | ||||
|     status: {default: "success"}, | ||||
|     btnTap: Function | ||||
|   }, | ||||
|   computed: { | ||||
|     result() { | ||||
|       let obj = { | ||||
|         image: this.image, | ||||
|         tips: this.tips, | ||||
|         btn: this.btn | ||||
|       } | ||||
|       if (this.status == "error") { | ||||
|         obj.image = this.$cdn + "result/fail.png" | ||||
|         obj.tips = this.tips || "提交失败!" | ||||
|       } | ||||
|       return obj | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     handleTap() { | ||||
|       this.btnTap && this.btnTap() | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiResult { | ||||
|   padding-top: 96px; | ||||
|   width: 100%; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
|   font-size: 36px; | ||||
|   font-weight: bold; | ||||
|  | ||||
|   & > image { | ||||
|     width: 192px; | ||||
|     height: 192px; | ||||
|   } | ||||
|  | ||||
|   .tips { | ||||
|     margin: 16px auto 0; | ||||
|     color: #333; | ||||
|   } | ||||
|  | ||||
|   .extra { | ||||
|     margin-top: 48px; | ||||
|   } | ||||
|  | ||||
|   .btn { | ||||
|     cursor: pointer; | ||||
|     margin-top: 80px; | ||||
|     width: calc(100% - 192px); | ||||
|     height: 88px; | ||||
|     background: #197DF0; | ||||
|     box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.02); | ||||
|     border-radius: 8px; | ||||
|     color: #FFF; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
|     font-weight: 500; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										87
									
								
								src/components/AiSearchPopup.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,87 @@ | ||||
| <template> | ||||
|   <section class="AiSearchPopup"> | ||||
|     <u-popup v-model="show" length="100%" closeable :mode="mode"> | ||||
|       <slot v-if="$slots.default"/> | ||||
|       <div class="searchPane" v-else> | ||||
|         <div class="title">{{ title }}</div> | ||||
|         <u-search v-model="search" :placeholder="placeholder" :show-action="false" @search="getList()" :focus="show"/> | ||||
|         <div class="result"> | ||||
|           <div class="option" v-for="(op,i) in list" :key="i" @tap="handleSelect(op)">{{ op[ops.label] }}</div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </u-popup> | ||||
|     <div @tap="show=true"> | ||||
|       <slot name="btn"/> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "AiSearchPopup", | ||||
|   props: { | ||||
|     title: {default: "搜索"}, | ||||
|     placeholder: {default: "请搜索"}, | ||||
|     ops: {default: () => ({label: 'label', search: 'name'})}, | ||||
|     url: String, | ||||
|     mode: {default: "right"} | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       show: false, | ||||
|       search: "", | ||||
|       list: [] | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getList() { | ||||
|       this.url && this.$instance.post(this.url, null, { | ||||
|         params: {[this.ops.search]: this.search} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.list = res.data | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleSelect(op) { | ||||
|       this.$emit('select', op) | ||||
|       this.show = false | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiSearchPopup { | ||||
|  | ||||
|  | ||||
|   ::v-deep .searchPane { | ||||
|     padding: 0 16px; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     height: 100%; | ||||
|  | ||||
|     .title { | ||||
|       width: 100%; | ||||
|       height: 100px; | ||||
|       text-align: center; | ||||
|       line-height: 100px; | ||||
|     } | ||||
|  | ||||
|     .result { | ||||
|       flex: 1; | ||||
|       min-height: 0; | ||||
|       overflow-y: auto; | ||||
|       padding-bottom: 30px; | ||||
|     } | ||||
|  | ||||
|     .option { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       height: 80px; | ||||
|       border-bottom: 1px solid #eee; | ||||
|       font-size: 32px; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										83
									
								
								src/components/AiSelect.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,83 @@ | ||||
| <template> | ||||
|   <section class="AiSelect"> | ||||
|     <div class="display" v-if="$slots.default" @tap="handleShowOptions"> | ||||
|       <slot/> | ||||
|     </div> | ||||
|     <div v-else class="display" @tap="handleShowOptions"> | ||||
|       <div class="selectedLabel" v-if="selectedLabel">{{ selectedLabel }}</div> | ||||
|       <i v-else>{{ placeholder }}</i> | ||||
|       <u-icon name="arrow-right" color="#ddd"/> | ||||
|     </div> | ||||
|     <u-select v-model="show" :list="options" :mode="mode" @confirm="handleConfirm"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "AiSelect", | ||||
|   props: { | ||||
|     value: String, | ||||
|     placeholder: {default: "请选择"}, | ||||
|     list: {default: () => []}, | ||||
|     mode: {default: "single-column"}, | ||||
|     dict: {default: ""}, | ||||
|     disabled: Boolean | ||||
|   }, | ||||
|   computed: { | ||||
|     selectedLabel() { | ||||
|       let label = this.options.find(e => e.value == this.value)?.label | ||||
|       return this.selected?.map(e => e.label)?.join(",") || label | ||||
|     }, | ||||
|     options() { | ||||
|       return this.dict ? this.$dict.getDict(this.dict).map(e => ({ | ||||
|         value: e.dictValue, | ||||
|         label: e.dictName | ||||
|       })) : this.list | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       show: false, | ||||
|       selected: [] | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     handleConfirm(v) { | ||||
|       this.selected = v | ||||
|       this.$emit("data", this.selected) | ||||
|       this.$forceUpdate() | ||||
|     }, | ||||
|     handleShowOptions() { | ||||
|       if (!this.disabled) this.show = true | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiSelect { | ||||
|   max-width: 100%; | ||||
|  | ||||
|   ::v-deep .u-icon { | ||||
|     margin-left: 8px; | ||||
|   } | ||||
|  | ||||
|   .display { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|  | ||||
|     .selectedLabel { | ||||
|       flex: 1; | ||||
|       min-width: 0; | ||||
|       overflow: hidden; | ||||
|       text-overflow: ellipsis; | ||||
|       white-space: nowrap; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   i { | ||||
|     font-style: normal; | ||||
|     color: $uni-text-color-grey; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										148
									
								
								src/components/AiSelectEnterprise/AiSelectEnterprise.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,148 @@ | ||||
| <template> | ||||
|   <div class="AiSelectEnterprise"> | ||||
|     <tree :checkList="checkList" :props="prop" @sendValue="(val)=>checkList = val" :multiple="multiple" :isCheck="true" | ||||
|           :rootId="rootId"/> | ||||
|     <div class="footer"> | ||||
|       <scroll-view scroll-x class="scroll" style="width: 100%;"> | ||||
|       </scroll-view> | ||||
|       <div class="btn" @click="confirm">确定选择</div> | ||||
|       <AiBack :visible="true" eventName="update:visible" :data="false" @click.native="confirm"></AiBack> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import tree from "./tree"; | ||||
|   import AiBack from "../AiBack"; | ||||
|  | ||||
|   export default { | ||||
|     name: "AiSelectEnterprise", | ||||
|     components: {tree, AiBack}, | ||||
|     props: { | ||||
|       value: { | ||||
|         type: Array, | ||||
|         default: () => [] | ||||
|       }, | ||||
|       multiple: { | ||||
|         type: Boolean, | ||||
|         default: true | ||||
|       }, | ||||
|       rootId: Object, | ||||
|     }, | ||||
|     data() { | ||||
|       return { | ||||
|         tree: [], | ||||
|         checkList: this.value, | ||||
|         prop: { | ||||
|           label: 'name', | ||||
|           multiple: this.multiple, | ||||
|         }, | ||||
|         map: {}, | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     created() { | ||||
|       uni.pageScrollTo({ | ||||
|         duration: 0, | ||||
|         scrollTop: 0 | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       confirm() { | ||||
|         let filter = [] | ||||
|         this.map = {} | ||||
|         this.recursion(this.checkList) | ||||
|         Object.keys(this.map).map(e => filter.push(this.map[e])) | ||||
|         this.$emit("change", filter) | ||||
|         this.$emit('update:visible', false) | ||||
|       }, | ||||
|  | ||||
|       recursion(arr) { | ||||
|         if (arr?.length) { | ||||
|           arr.map(e => { | ||||
|             if ((e.type == 0 || e.openId) && e.checked && !this.map[e.id]) { | ||||
|               this.map[e.id] = e | ||||
|               this.recursion(e.childrenUser) | ||||
|             } | ||||
|             if (e.childrenDept?.length) { | ||||
|               this.recursion(e.childrenDept) | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .AiSelectEnterprise { | ||||
|     min-height: 100%; | ||||
|     background-color: #F5F5F5; | ||||
|     position: relative; | ||||
|  | ||||
|     .footer { | ||||
|       width: 100%; | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       z-index: 10; | ||||
|       background: #F4F8FB; | ||||
|       position: fixed; | ||||
|       left: 0; | ||||
|       bottom: 0; | ||||
|       box-sizing: border-box; | ||||
|       padding: 0 32px; | ||||
|  | ||||
|       .scroll { | ||||
|         height: 118px; | ||||
|  | ||||
|         ::v-deep .uni-scroll-view-content { | ||||
|           display: flex; | ||||
|           align-items: center; | ||||
|  | ||||
|           .tag { | ||||
|             width: 236px; | ||||
|             height: 72px; | ||||
|             background: #EAEEF1; | ||||
|             border-radius: 8px; | ||||
|             display: flex; | ||||
|             align-items: center; | ||||
|             margin-right: 16px; | ||||
|  | ||||
|             & > img { | ||||
|               width: 48px; | ||||
|               height: 45px; | ||||
|               margin-right: 8px; | ||||
|               flex-shrink: 0; | ||||
|             } | ||||
|  | ||||
|             & > label { | ||||
|               width: 148px; | ||||
|               height: 42px; | ||||
|               font-size: 30px; | ||||
|               font-weight: 600; | ||||
|               color: #333333; | ||||
|               overflow: hidden; | ||||
|               text-overflow: ellipsis; | ||||
|               white-space: nowrap; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .btn { | ||||
|         width: 192px; | ||||
|         height: 80px; | ||||
|         background: #1365DD; | ||||
|         border-radius: 4px; | ||||
|         z-index: 10; | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         font-size: 32px; | ||||
|         color: #FFFFFF; | ||||
|         margin-left: 8px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
							
								
								
									
										481
									
								
								src/components/AiSelectEnterprise/tree.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,481 @@ | ||||
| <template> | ||||
|   <div class="tree"> | ||||
|     <ai-top-fixed> | ||||
|       <div class="top pad"> | ||||
|         <u-search v-if="searchIf" placeholder="搜索" @change="confirmSearch" @search="confirmSearch" :clearabled="true" | ||||
|                   v-model="keyword" :show-action="false" @clear="clear"></u-search> | ||||
|         <u-tabs :list="list" :current="current" item-width="50%" height="96" bar-width="192" | ||||
|                 @change="tabChange"></u-tabs> | ||||
|       </div> | ||||
|     </ai-top-fixed> | ||||
|  | ||||
|     <div class="tree-list"> | ||||
|       <scroll-view scroll-x class="scroll pad" style="width:100%" :scroll-left="scrollLeft"> | ||||
|         <div v-for="(item,index) in parent" class="inline-item" :key="index"> | ||||
|           <div class="inline-item" v-if="index==0" @click.stop="backTree(item,-1)"> | ||||
|             <text v-if="index==parent.length-1&&!isSear" class="none">可选范围</text> | ||||
|             <text v-else class="active">可选范围</text> | ||||
|           </div> | ||||
|           <div v-if="index==0 && isSear" @click.stop="backTree(item,-2)" | ||||
|                :class="[index==parent.length-1 && isSear] ? 'none inline-item':'active inline-item'"> | ||||
|             <span style="margin: 0 8px">/</span> | ||||
|             搜索结果 | ||||
|           </div> | ||||
|           <div class="inline-item" @click.stop="backTree(item,index)" v-if="index!=0"> | ||||
|             <span style="margin: 0 8px">/</span> | ||||
|             <text v-if="index==parent.length-1" class="none inline-item"> | ||||
|               {{item[tag]}} | ||||
|             </text> | ||||
|             <text v-else class="active"> | ||||
|               {{item[tag]}} | ||||
|             </text> | ||||
|           </div> | ||||
|         </div> | ||||
|       </scroll-view> | ||||
|       <div class="container-list"> | ||||
|         <div class="common" v-for="(item, index) in tree" @click.stop="toNext(item)" :key="index"> | ||||
|           <label class="content"> | ||||
|             <div class="checkbox" v-if="multiple" @click.stop="checkboxChange(item,index)"> | ||||
|               <img :src="$cdn + 'common/xzh.png'" v-if="item.checked" alt=""> | ||||
|               <img :src="$cdn + 'common/xzn.png'" v-else alt=""> | ||||
|             </div> | ||||
|             <div class="checkbox" v-if="!multiple && (item.type==0 || item.openId)" @click.stop="checkbox(item,index)"> | ||||
|               <img :src="$cdn + 'common/xzh.png'" v-if="item.checked" alt=""> | ||||
|               <img :src="$cdn + 'common/xzn.png'" v-else alt=""> | ||||
|             </div> | ||||
|             <div class="person" v-if="item.type==0"> | ||||
|               <u-avatar :src="item.avatar || ($cdn + 'common/xztx.png')" mode="square" :size="74"></u-avatar> | ||||
|             </div> | ||||
|  | ||||
|             <u-row justify="between" style="width: 100%;"> | ||||
|               <div class="word" v-if="tag=='name'"> | ||||
|                 <img :src="$cdn + 'common/xzbq.png'" v-if="item.type==1" alt=""> | ||||
|                 <span class="ellipsis">{{item[tag]}}</span> | ||||
|               </div> | ||||
|               <div class="word" v-else-if="tag=='tagname'"> | ||||
|                 <template v-if="!item.openId"> | ||||
|                   <img :src="$cdn + 'common/xzbqbottom.png'" alt=""> | ||||
|                   <span class="ellipsis">{{item[tag]}}</span> | ||||
|                 </template> | ||||
|                 <template v-else> | ||||
|                   <u-avatar :src="item.avatar || ($cdn + 'common/xztx.png')" mode="square" :size="74" | ||||
|                             style="margin: 0 17px;"></u-avatar> | ||||
|                   <span class="ellipsis">{{item["name"]}}</span> | ||||
|                 </template> | ||||
|               </div> | ||||
|               <div class="right" | ||||
|                    v-if="item.type==1 && (item.childrenDept.length || item.childrenUser.length) && tag=='name'"></div> | ||||
|               <div class="right" v-if="tag=='tagname' && !item.openId"></div> | ||||
|             </u-row> | ||||
|           </label> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import AiTopFixed from "../AiTopFixed"; | ||||
|  | ||||
|   export default { | ||||
|     name: "tree", | ||||
|     components: {AiTopFixed}, | ||||
|     props: { | ||||
|       checkList: { | ||||
|         type: Array, | ||||
|         default: () => [] | ||||
|       }, | ||||
|       searchIf: { | ||||
|         type: Boolean, | ||||
|         default: () => true | ||||
|       }, | ||||
|       multiple: { | ||||
|         type: Boolean, | ||||
|         default: true | ||||
|       }, | ||||
|       rootId: Object, | ||||
|     }, | ||||
|     data() { | ||||
|       return { | ||||
|         isSear: false, | ||||
|         tree: [], | ||||
|         parent: [1], | ||||
|         searchResult: [], | ||||
|         allData: [], | ||||
|         newCheckList: this.checkList, | ||||
|         scrollLeft: Infinity, | ||||
|         keyword: "", | ||||
|         current: 0, | ||||
|         tag: "name", | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       tabChange(e) { | ||||
|         this.tag = e == 0 ? "name" : "tagname" | ||||
|         this.current = e | ||||
|         this.parent = [1] | ||||
|         // this.newCheckList = [] | ||||
|         this.getTree() | ||||
|       }, | ||||
|  | ||||
|       clear() { | ||||
|         this.keyword = "" | ||||
|         this.tree = this.allData | ||||
|         this.parent = [1] | ||||
|         this.isSear = false | ||||
|       }, | ||||
|  | ||||
|       checkboxChange(item, index) { | ||||
|         if (item.checked) { | ||||
|           this.$set(this.tree[index], 'checked', false) | ||||
|           this.delChild(item) | ||||
|           for (let index = 0, n = this.newCheckList.length; index < n; index++) { | ||||
|             let temp = this.newCheckList[index]; | ||||
|             if (temp.id == item.id) { | ||||
|               this.newCheckList.splice(index, 1) | ||||
|               break | ||||
|             } | ||||
|           } | ||||
|         } else { | ||||
|           (item.type == 0 || item.openId) && this.newCheckList.push(item) | ||||
|           this.$set(this.tree[index], 'checked', true) | ||||
|           this.chooseChild(item) | ||||
|         } | ||||
|         this.$emit('sendValue', this.newCheckList) | ||||
|       }, | ||||
|  | ||||
|       delUser(id) { | ||||
|         for (let i = 0, len = this.newCheckList.length; i < len; i++) { | ||||
|           if (this.newCheckList[i].id === id) { | ||||
|             return this.newCheckList.splice(i, 1) | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|  | ||||
|       chooseChild(arr) { | ||||
|         if (arr.childrenDept?.length) { | ||||
|           for (let i = 0, len = arr.childrenDept.length; i < len; i++) { | ||||
|             let item = arr.childrenDept[i] | ||||
|             item.checked = true | ||||
|             this.newCheckList.push(item) | ||||
|             this.chooseChild(item) | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         if (arr.childrenUser?.length) { | ||||
|           for (let i = 0, len = arr.childrenUser.length; i < len; i++) { | ||||
|             let item = arr.childrenUser[i] | ||||
|             item.checked = true | ||||
|             this.newCheckList.push(item) | ||||
|             this.chooseChild(item) | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         if (arr.users?.length) { | ||||
|           for (let i = 0, len = arr.users.length; i < len; i++) { | ||||
|             let item = arr.users[i] | ||||
|             item.checked = true | ||||
|             this.newCheckList.push(item) | ||||
|             this.chooseChild(item) | ||||
|           } | ||||
|         } | ||||
|         this.newCheckList = Array.from(new Set(this.newCheckList)) | ||||
|       }, | ||||
|  | ||||
|       delChild(arr) { | ||||
|         if (arr.childrenDept?.length) { | ||||
|           for (let i = 0, len = arr.childrenDept.length; i < len; i++) { | ||||
|             let item = arr.childrenDept[i]; | ||||
|             item.checked = false | ||||
|             for (let index = 0, n = this.newCheckList.length; index < n; index++) { | ||||
|               let temp = this.newCheckList[index]; | ||||
|               if (temp.id == item.id) { | ||||
|                 this.newCheckList.splice(index, 1) | ||||
|                 break | ||||
|               } | ||||
|             } | ||||
|             this.delChild(item) | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         if (arr.childrenUser?.length) { | ||||
|           for (let i = 0, len = arr.childrenUser.length; i < len; i++) { | ||||
|             let item = arr.childrenUser[i]; | ||||
|             item.checked = false | ||||
|             for (let index = 0, n = this.newCheckList.length; index < n; index++) { | ||||
|               let temp = this.newCheckList[index]; | ||||
|               if (temp.id == item.id) { | ||||
|                 this.newCheckList.splice(index, 1) | ||||
|                 break | ||||
|               } | ||||
|             } | ||||
|             this.delChild(item) | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|  | ||||
|       //单选 | ||||
|       checkbox(item, index) { | ||||
|         let status = !this.tree[index].checked | ||||
|         this.$set(this.tree[index], 'checked', status) | ||||
|         if (this.newCheckList.length <= 0) { | ||||
|           this.newCheckList = [this.tree[index]] | ||||
|         } else if (this.newCheckList.length == 1) { | ||||
|           this.tree.forEach(item => { | ||||
|             if (item.id != this.tree[index].id) { | ||||
|               item.checked = false | ||||
|             } | ||||
|           }) | ||||
|           this.newCheckList = [] | ||||
|           if (this.tree[index].checked) { | ||||
|             this.newCheckList.push(this.tree[index]) | ||||
|           } | ||||
|         } | ||||
|         this.$emit('sendValue', this.newCheckList) | ||||
|       }, | ||||
|  | ||||
|       toNext(item) { | ||||
|         if (this.tag == "name") { | ||||
|           if (item.type == 1 && (item["childrenDept"].length || item["childrenUser"].length)) { | ||||
|             this.tree = [] | ||||
|             if (item["childrenDept"].length) { | ||||
|               this.tree = item["childrenDept"] | ||||
|             } | ||||
|  | ||||
|             if (item["childrenUser"].length) { | ||||
|               this.tree = [...this.tree, ...item["childrenUser"]] | ||||
|             } | ||||
|             this.checkIf() | ||||
|             if (this.parent[0].id !== item.id) { | ||||
|               this.parent.push(item) | ||||
|             } | ||||
|           } | ||||
|         } else if (this.tag == "tagname" && !item.openId) { | ||||
|           this.tree = item.users | ||||
|           if (this.parent[0].id !== item.id) { | ||||
|             this.parent.push(item) | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         this.$nextTick(() => { | ||||
|           this.scrollLeft += 200 | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       checkIf() { | ||||
|         for (let i = 0, len = this.tree.length; i < len; i++) { | ||||
|           for (let j = 0, lens = this.newCheckList.length; j < lens; j++) { | ||||
|             if (this.newCheckList[j].id == this.tree[i].id) { | ||||
|               this.$set(this.tree[i], 'checked', true) | ||||
|               break | ||||
|             } else { | ||||
|               this.$set(this.tree[i], 'checked', false) | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|  | ||||
|       confirmSearch(val) { | ||||
|         this.searchResult = [] | ||||
|         this.search(this.tree, val) | ||||
|         this.isSear = true | ||||
|         this.parent.splice(1, Infinity) | ||||
|         this.tree = this.searchResult | ||||
|         if(!val) this.clear() | ||||
|       }, | ||||
|  | ||||
|       search(data, keyword) { | ||||
|         if (data.length) { | ||||
|           for (let i = 0, len = data.length; i < len; i++) { | ||||
|             if (data[i].name?.indexOf(keyword) != -1) { | ||||
|               this.searchResult.push(data[i]) | ||||
|             } | ||||
|             if (data[i]["childrenDept"]?.length || data[i]["childrenUser"]?.length) { | ||||
|               this.search(data[i]["childrenDept"].concat(data[i]["childrenUser"]), keyword) | ||||
|             } | ||||
|             if (data[i]["users"]?.length) { | ||||
|               this.search(data[i]["users"], keyword) | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|  | ||||
|       backTree(item, index) { | ||||
|         if (index == -1) { | ||||
|           this.tree = this.allData | ||||
|           this.parent.splice(1, Infinity) | ||||
|           this.isSear = false | ||||
|           this.keyword = "" | ||||
|         } else if (index == -2) { | ||||
|           this.tree = this.searchResult | ||||
|           this.parent.splice(1, Infinity) | ||||
|         } else { | ||||
|           if (this.parent.length - index > 2) { | ||||
|             this.parent.forEach((item, i) => { | ||||
|               if (i > index) { | ||||
|                 this.parent.splice(i, Infinity) | ||||
|               } | ||||
|             }) | ||||
|           } else if (index != this.parent.length - 1) { | ||||
|             this.parent.splice(this.parent.length - 1, 1) | ||||
|           } | ||||
|           this.tree = item["childrenDept"].concat(item["childrenUser"] || []) | ||||
|         } | ||||
|         if (this.multiple) return | ||||
|         this.checkIf() | ||||
|       }, | ||||
|  | ||||
|       getTree() { | ||||
|         this.$http.post(this.current == 0 ? "/app/wxcp/wxuser/tree" : "/app/wxcp/wxtag/tree", null, { | ||||
|           params: { | ||||
|             rootId: this.rootId | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res && res.data) { | ||||
|             let result = this.tag == 'name' ? [res.data] : res.data | ||||
|             this.tree = result | ||||
|             this.allData = result | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|       list() { | ||||
|         return [ | ||||
|           {name: "组织架构"}, | ||||
|           {name: "标签"} | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     created() { | ||||
|       this.getTree() | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|  | ||||
|   .tree { | ||||
|     min-height: 100%; | ||||
|     background-color: #F5F5F5; | ||||
|  | ||||
|     .top { | ||||
|       background-color: #FFFFFF; | ||||
|     } | ||||
|  | ||||
|     .tree-list { | ||||
|       margin-top: 24px; | ||||
|       background-color: #FFFFFF; | ||||
|  | ||||
|       .scroll { | ||||
|         white-space: nowrap; | ||||
|         border-bottom: 1px solid #f4f4f4; | ||||
|  | ||||
|         .inline-item { | ||||
|           height: 112px; | ||||
|           font-size: 30px; | ||||
|           display: inline-block; | ||||
|           line-height: 112px; | ||||
|  | ||||
|           .active { | ||||
|             color: #4297ED !important; | ||||
|             font-weight: 600; | ||||
|           } | ||||
|  | ||||
|           .none { | ||||
|             color: #666666; | ||||
|             font-weight: 600; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .container-list { | ||||
|         min-height: 1000px; | ||||
|         overflow-y: scroll; | ||||
|         overflow-x: hidden; | ||||
|  | ||||
|         .common { | ||||
|           background-color: #fff; | ||||
|           border-bottom: 1px solid #f4f4f4; | ||||
|           box-sizing: border-box; | ||||
|           padding: 0 30px; | ||||
|  | ||||
|           .content { | ||||
|             display: flex; | ||||
|             align-items: center; | ||||
|             height: 100px; | ||||
|             width: 100%; | ||||
|             line-height: 100px; | ||||
|             position: relative; | ||||
|             font-size: 32px; | ||||
|  | ||||
|             .right { | ||||
|               width: 16px; | ||||
|               height: 16px; | ||||
|               border-right: 4px solid #CCCCCC; | ||||
|               border-top: 4px solid #CCCCCC; | ||||
|               transform: rotate(45deg); | ||||
|             } | ||||
|  | ||||
|             .word { | ||||
|               display: flex; | ||||
|               align-items: center; | ||||
|  | ||||
|               & > img { | ||||
|                 width: 74px; | ||||
|                 height: 74px; | ||||
|                 margin: 0 34px; | ||||
|               } | ||||
|  | ||||
|               .ellipsis{ | ||||
|                 width: 450px; | ||||
|                 overflow: hidden; | ||||
|                 text-overflow: ellipsis; | ||||
|                 white-space: nowrap; | ||||
|               } | ||||
|             } | ||||
|  | ||||
|             .checkbox { | ||||
|               position: relative; | ||||
|               display: flex; | ||||
|               align-items: center; | ||||
|  | ||||
|               & > img { | ||||
|                 width: 48px; | ||||
|                 height: 48px; | ||||
|                 border-radius: 50%; | ||||
|               } | ||||
|  | ||||
|               .color { | ||||
|                 color: #00aaff; | ||||
|                 background-color: #00aaff; | ||||
|               } | ||||
|             } | ||||
|  | ||||
|             .person { | ||||
|               display: flex; | ||||
|               align-items: center; | ||||
|               color: #f57a00; | ||||
|               font-size: 36px; | ||||
|               text-align: center; | ||||
|               margin: 0 34px; | ||||
|               flex-shrink: 0; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     ::v-deep .content { | ||||
|       padding: 0 !important; | ||||
|     } | ||||
|  | ||||
|     .pad { | ||||
|       box-sizing: border-box; | ||||
|       padding: 20px 32px 0; | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
							
								
								
									
										63
									
								
								src/components/AiTabbar.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,63 @@ | ||||
| <template> | ||||
|   <section class="AiTabbar"> | ||||
|     <div class="tabPane" v-for="(op,i) in tabbars" :key="i" | ||||
|          @click="$emit('update:active',i)"> | ||||
|       <img :src="op.icon" alt=""/> | ||||
|       <span :class="{active:i==active}">{{ op.text }}</span> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "AiTabbar", | ||||
|   props: { | ||||
|     active: {default: 0}, | ||||
|     list: {default: () => []}, | ||||
|   }, | ||||
|   computed: { | ||||
|     tabbars() { | ||||
|       return this.list.map((e, i) => ({ | ||||
|         ...e, | ||||
|         icon: i == this.active ? e.selectedIconPath : e.iconPath | ||||
|       })) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiTabbar { | ||||
|   height: 98px; | ||||
|   width: 100%; | ||||
|   position: fixed; | ||||
|   bottom: 0; | ||||
|   background: #FFFFFF; | ||||
|   border-top: 1px solid #ddd; | ||||
|   display: flex; | ||||
|   z-index: 9; | ||||
|  | ||||
|   .tabPane { | ||||
|     flex: 1; | ||||
|     min-width: 0; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|     font-size: 22px; | ||||
|     cursor: pointer; | ||||
|  | ||||
|     & > img { | ||||
|       height: 44px; | ||||
|     } | ||||
|  | ||||
|     & > span { | ||||
|       color: #C4CAD4; | ||||
|  | ||||
|       &.active { | ||||
|         color: #3267F0; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										53
									
								
								src/components/AiTable.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,53 @@ | ||||
| <template> | ||||
|   <section class="AiTable"> | ||||
|     <u-table color="#333"> | ||||
|       <u-tr> | ||||
|         <u-th v-for="(col,i) in colConfigs" :key="i" :width="col.width">{{ col.label }}</u-th> | ||||
|       </u-tr> | ||||
|       <u-tr v-for="(row,j) in data" :key="j"> | ||||
|         <u-td v-for="(col,i) in colConfigs" :key="i" :width="col.width"> | ||||
|           <slot v-if="col.slot" :name="col.slot"/> | ||||
|           <p v-else-if="col.dict">{{ $dict.getLabel(col.dict, row[col.prop]) }}</p> | ||||
|           <p v-else>{{ row[col.prop] || "-" }}</p> | ||||
|         </u-td> | ||||
|       </u-tr> | ||||
|     </u-table> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import UTable from "../uview/components/u-table/u-table"; | ||||
| import UTd from "../uview/components/u-td/u-td"; | ||||
| import UTh from "../uview/components/u-th/u-th"; | ||||
| import UTr from "../uview/components/u-tr/u-tr"; | ||||
|  | ||||
| export default { | ||||
|   name: "AiTable", | ||||
|   components: {UTr, UTh, UTd, UTable}, | ||||
|   props: { | ||||
|     data: {default: () => []}, | ||||
|     colConfigs: {default: () => []}, | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiTable { | ||||
|   border-radius: 8px; | ||||
|   min-height: 100px; | ||||
|   overflow: hidden; | ||||
|  | ||||
|   .u-table, .u-th { | ||||
|     border-color: #D0D4DC !important; | ||||
|   } | ||||
|  | ||||
|   .u-th { | ||||
|     background-color: #DFE6F4; | ||||
|     color: #646D7F; | ||||
|   } | ||||
|  | ||||
|   .u-tr { | ||||
|     height: 80px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										81
									
								
								src/components/AiTabs.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,81 @@ | ||||
| <template> | ||||
|   <section class="AiTabs" :class="{wrap}"> | ||||
|     <div class="tabItem" v-for="(op,i) in ops" :key="i" | ||||
|          :class="{active:value==op.value,plain}" | ||||
|          :style="{width:itemWidth}" | ||||
|          @tap="$emit('change',op.value)"> | ||||
|       {{ op.name }} | ||||
|     </div> | ||||
|     <div class="end"> | ||||
|       <slot name="end"/> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "AiTabs", | ||||
|   model: { | ||||
|     prop: "value", | ||||
|     event: "change" | ||||
|   }, | ||||
|   props: { | ||||
|     value: {default: ""}, | ||||
|     ops: {default: () => []}, | ||||
|     wrap: Boolean, | ||||
|     plain: Boolean, | ||||
|     itemWidth: String | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiTabs { | ||||
|   display: flex; | ||||
|   flex: 1; | ||||
|   min-width: 0; | ||||
|   max-height: 240px; | ||||
|   overflow-y: auto; | ||||
|  | ||||
|   &.wrap { | ||||
|     flex-wrap: wrap; | ||||
|   } | ||||
|  | ||||
|   .tabItem { | ||||
|     flex-shrink: 0; | ||||
|     min-width: 144px; | ||||
|     max-width: 100%; | ||||
|     min-height: 64px; | ||||
|     font-size: 28px; | ||||
|     font-weight: 400; | ||||
|     color: #666; | ||||
|     background: #FFFFFF; | ||||
|     border-radius: 4px; | ||||
|     border: 1px solid #CCC; | ||||
|     text-align: center; | ||||
|     line-height: 64px; | ||||
|     margin-bottom: 16px; | ||||
|     margin-right: 16px; | ||||
|     padding: 0 16px; | ||||
|     box-sizing: border-box; | ||||
|  | ||||
|     &.active { | ||||
|       border-color: $uni-color-primary; | ||||
|       color: $uni-color-primary; | ||||
|  | ||||
|       &.plain { | ||||
|         color: #fff; | ||||
|         border-color: transparent; | ||||
|         background: $uni-color-primary; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .end { | ||||
|     flex: 1; | ||||
|     min-width: 0; | ||||
|     display: flex; | ||||
|     justify-content: flex-end; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										82
									
								
								src/components/AiTextarea.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,82 @@ | ||||
| <template> | ||||
|   <section class="AiTextarea" :class="{border}"> | ||||
|     <u-input type="textarea" v-bind="$attrs" :value="value" :maxlength="maxlength" | ||||
|              @input="handleInput" :disabled="disabled"/> | ||||
|     <div class="bottomBar"> | ||||
|       <div class="leftPane"> | ||||
|         <slot name="bar"/> | ||||
|       </div> | ||||
|       <div v-if="!!maxlength">{{ value.length }}/{{ maxlength }}</div> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import UInput from "../uview/components/u-input/u-input"; | ||||
|  | ||||
| export default { | ||||
|   name: "AiTextarea", | ||||
|   components: {UInput}, | ||||
|   model: { | ||||
|     prop: "value", | ||||
|     event: "change" | ||||
|   }, | ||||
|   props: { | ||||
|     value: {default: ""}, | ||||
|     maxlength: {default: 0}, | ||||
|     border: Boolean, | ||||
|     disabled: Boolean | ||||
|   }, | ||||
|   methods: { | ||||
|     handleInput(v) { | ||||
|       this.$emit('change', v) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiTextarea { | ||||
|   width: 100%; | ||||
|   position: relative; | ||||
|  | ||||
|   &.border { | ||||
|     ::v-deep textarea { | ||||
|       border-radius: 4px; | ||||
|       border: 1px solid #e2e1e1; | ||||
|       padding: 16px 16px 36px; | ||||
|       box-sizing: border-box; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     .bottomBar { | ||||
|       position: absolute; | ||||
|       bottom: 8px; | ||||
|       right: 16px; | ||||
|     } | ||||
|  | ||||
|     ::v-deep .u-input__right-icon { | ||||
|       position: absolute; | ||||
|       top: 50%; | ||||
|       right: 16px; | ||||
|       transform: translateY(-50%); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .bottomBar { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     justify-content: space-between; | ||||
|     min-height: 36px; | ||||
|     color: #999999; | ||||
|  | ||||
|     .leftPane { | ||||
|       display: flex; | ||||
|  | ||||
|       & > * + * { | ||||
|         margin-left: 32px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										63
									
								
								src/components/AiTopFixed.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,63 @@ | ||||
| <template> | ||||
|   <section class="AiTopFixed" :style="{background}"> | ||||
|     <!--占位区--> | ||||
|     <div class="placeholder"> | ||||
|       <div v-if="$slots.tabs"> | ||||
|         <slot name="tabs"/> | ||||
|       </div> | ||||
|       <div class="content" v-if="$slots.default"> | ||||
|         <slot/> | ||||
|       </div> | ||||
|     </div> | ||||
|     <!--悬浮区--> | ||||
|     <div class="fixed" :style="{background}"> | ||||
|       <div v-if="$slots.tabs"> | ||||
|         <slot name="tabs"/> | ||||
|       </div> | ||||
|       <div class="content" v-if="$slots.default"> | ||||
|         <slot/> | ||||
|       </div> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "AiTopFixed", | ||||
|   props: { | ||||
|     background: {default: "#fff"} | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiTopFixed { | ||||
|   width: 100%; | ||||
|  | ||||
|   & > div { | ||||
|     width: 100%; | ||||
|   } | ||||
|  | ||||
|   .fixed { | ||||
|     width: 100%; | ||||
|     top: 0; | ||||
|     position: fixed; | ||||
|     z-index: 9; | ||||
|   } | ||||
|  | ||||
|   .placeholder { | ||||
|     visibility: hidden; | ||||
|     opacity: 0; | ||||
|   } | ||||
|  | ||||
|   .content { | ||||
|     padding: 20px 32px; | ||||
|     box-sizing: border-box; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .u-search { | ||||
|     box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.02); | ||||
|     margin-bottom: 32px !important; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										229
									
								
								src/components/AiUploader.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,229 @@ | ||||
| <template> | ||||
|   <div class="ai-uploader"> | ||||
|     <div class="fileList"> | ||||
|       <div class="item" v-for="(item, i) in fileList" :key="i"> | ||||
|         <template v-if="type == 'image'"> | ||||
|           <ai-image :src="item.url" :preview="preview"/> | ||||
|           <div class="info"> | ||||
|             <i>{{ item.fileSizeStr }}</i> | ||||
|           </div> | ||||
|         </template> | ||||
|         <template v-else> | ||||
|           <ai-image :preview="preview" :file="item"/> | ||||
|           <div class="info"> | ||||
|             <span>{{ item.name }} </span> | ||||
|             <i>{{ item.fileSizeStr }}</i> | ||||
|           </div> | ||||
|         </template> | ||||
|         <template v-if="!disabled"> | ||||
|           <div btn @tap="handleReUpload(i)"> | ||||
|             重新上传 | ||||
|           </div> | ||||
|           <div btn @tap="remove(i)"> | ||||
|             删除 | ||||
|           </div> | ||||
|         </template> | ||||
|       </div> | ||||
|       <div v-if="!disabled&&(fileList.length == 0 || (multiple && fileList.length < limit))" class="default" | ||||
|            @click="upload"> | ||||
|         <i class="iconfont iconfont-iconAdd"/> | ||||
|         <span>{{ placeholder }}</span> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapState} from 'vuex' | ||||
| import AiImage from './AiImage' | ||||
|  | ||||
| export default { | ||||
|   name: 'AiUploader', | ||||
|   components: {AiImage}, | ||||
|   props: { | ||||
|     limit: {default: 1}, //数量 | ||||
|     placeholder: {default: '添加图片'}, // 文字提示 | ||||
|     type: {default: 'image'}, // 文件类型,image还是file | ||||
|     multiple: { | ||||
|       type: Boolean, | ||||
|       default: false, | ||||
|     }, | ||||
|     fileId: String, | ||||
|     mediaId: String, | ||||
|     def: {default: () => []}, | ||||
|     action: {default: '/app/wxcp/upload/uploadFile'}, | ||||
|     preview: Boolean, | ||||
|     size: {default: 0}, | ||||
|     disabled: Boolean | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['baseURL', 'token']), | ||||
|     errorImage() { | ||||
|       return this.$cdn + 'file.png' | ||||
|     }, | ||||
|   }, | ||||
|   watch: { | ||||
|     def: { | ||||
|       handler(v) { | ||||
|         if (!!v?.toString() && v?.url) { | ||||
|           if (this.multiple) { | ||||
|             this.fileList = v | ||||
|           } else { | ||||
|             this.fileList = [v] | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       immediate: true, | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       fileList: [], | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     remove(index) { | ||||
|       this.fileList.splice(index, 1) | ||||
|       this.$emit('list', this.fileList) | ||||
|     }, | ||||
|     upload(wait) { | ||||
|       let params = { | ||||
|         count: this.limit, | ||||
|         sizeType: ['compressed'], | ||||
|         sourceType: ['album', 'camera'], | ||||
|         success: (res) => { | ||||
|           let count = this.fileList?.length + (res.tempFiles?.length || res.tempFile ? 1 : 0) | ||||
|           if (count > this.limit && this.limit !== 1) { | ||||
|             return this.$u.toast(`不能超过${this.limit}个`) | ||||
|           } | ||||
|           if (res.tempFiles) { | ||||
|             res.tempFiles?.map((item) => { | ||||
|               this.uploadFile(item) | ||||
|             }) | ||||
|           } else if (res?.tempFile) { | ||||
|             this.uploadFile(res.tempFile) | ||||
|           } | ||||
|         }, | ||||
|       } | ||||
|       typeof wait == 'function' && wait() | ||||
|       if (this.type == 'image') { | ||||
|         uni.chooseImage(params) | ||||
|       } else if (this.type == 'video') { | ||||
|         uni.chooseVideo(params) | ||||
|       } else { | ||||
|         uni.chooseFile(params) | ||||
|       } | ||||
|     }, | ||||
|     uploadFile(img) { | ||||
|       if (this.size > 0 && img.size > this.size) { | ||||
|         return this.$u.toast(`不能超过${Math.ceil(this.size / 1024 / 1024)}MB`) | ||||
|       } | ||||
|       uni.showLoading({title: '上传中'}) | ||||
|       let formData = new FormData() | ||||
|       formData.append('file', img) | ||||
|       this.$http | ||||
|       .post(this.action, formData, { | ||||
|         params: {type: this.type}, | ||||
|       }) | ||||
|       .then((res) => { | ||||
|         uni.hideLoading() | ||||
|         if (res?.data) { | ||||
|           this.$emit('data', res.data) | ||||
|           this.$u.toast('上传成功!') | ||||
|           if (this.action == '/app/wxcp/upload/uploadFile') { | ||||
|             this.$emit('update:mediaId', res.data?.media?.mediaId) | ||||
|             this.$emit('update:fileId', res.data.file.id) | ||||
|             this.fileList.push(res.data.file) | ||||
|           } else if (this.action == '/admin/file/add2') { | ||||
|             let info = res.data | ||||
|             this.$emit('update:fileId', info?.id) | ||||
|             this.fileList.push(res.data) | ||||
|           } | ||||
|           this.$emit("update:def", this.fileList) | ||||
|           this.$emit("list", this.fileList) | ||||
|         } else { | ||||
|           this.$u.toast(res.msg) | ||||
|         } | ||||
|       }) | ||||
|       .catch(() => uni.hideLoading()) | ||||
|     }, | ||||
|     handleReUpload(i) { | ||||
|       this.upload(() => this.remove(i)) | ||||
|     }, | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .ai-uploader { | ||||
|   width: 100%; | ||||
|   line-height: normal; | ||||
|   margin-bottom: 16px; | ||||
|  | ||||
|   .fileList { | ||||
|     .item { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       margin-bottom: 10px; | ||||
|  | ||||
|       image { | ||||
|         width: 160px; | ||||
|         height: 160px; | ||||
|       } | ||||
|  | ||||
|       i { | ||||
|         font-style: normal; | ||||
|         color: #9b9b9b; | ||||
|       } | ||||
|  | ||||
|       .info { | ||||
|         flex: 1; | ||||
|         min-width: 0; | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         align-items: flex-start; | ||||
|  | ||||
|         & > span { | ||||
|           overflow: hidden; | ||||
|           text-overflow: ellipsis; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       div[btn] { | ||||
|         color: $uni-color-primary; | ||||
|       } | ||||
|  | ||||
|       div:nth-child(4) { | ||||
|         color: #f72c27; | ||||
|       } | ||||
|  | ||||
|       & > * + * { | ||||
|         margin-left: 20px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .default { | ||||
|       width: 240px; | ||||
|       height: 240px; | ||||
|       box-sizing: border-box; | ||||
|       border-radius: 8px; | ||||
|       background: #f3f4f7; | ||||
|       color: #89b; | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       justify-content: center; | ||||
|       align-items: center; | ||||
|  | ||||
|       .iconfont-iconAdd { | ||||
|         font-size: 64px; | ||||
|       } | ||||
|  | ||||
|       span { | ||||
|         display: block; | ||||
|         text-align: center; | ||||
|         font-size: 28px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										185
									
								
								src/components/AiVideo.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,185 @@ | ||||
| <template> | ||||
| 	<view class="imt-audio"> | ||||
| 		<view class="audio-wrapper"> | ||||
| 			<view class="audio-number">{{format(current)}}</view> | ||||
| 			<slider class="audio-slider" :activeColor="color" block-size="16" :value="current" :max="duration || 10" @changing="seek=true,current=$event.detail.value" @change="audio.seek($event.detail.value)"></slider> | ||||
| 			<view class="audio-number">{{format(duration)}}</view> | ||||
| 		</view> | ||||
| 		<view class="audio-control-wrapper" :style="{color}"> | ||||
|       <image | ||||
|         class="audio-control audio-control-switch" | ||||
|         @click="audio.paused?play():audio.pause()" | ||||
|         :src="paused ? playImg : stopImg" /> | ||||
|       <p>{{ paused ? '点击播放' : '点击停止播放' }}</p> | ||||
| 		</view> | ||||
| 	</view> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import stopImg from '../pages/resourcesManage/img/stop-img.png' | ||||
|   import playImg from '../pages/resourcesManage/img/play-icon.png' | ||||
| 	export default { | ||||
| 		data() { | ||||
| 			return { | ||||
| 				audio: uni.createInnerAudioContext(), | ||||
| 				current: 0, //当前进度(s) | ||||
| 				duration: 0, //总时长(s) | ||||
| 				paused: true, //是否处于暂停状态 | ||||
| 				loading: false, //是否处于读取状态 | ||||
| 				seek: false, | ||||
|         stopImg, | ||||
|         playImg | ||||
| 			} | ||||
| 		}, | ||||
| 		props: { | ||||
| 			src: String, //音频链接 | ||||
| 			autoplay: Boolean, //是否自动播放 | ||||
| 			continue: Boolean, //播放完成后是否继续播放下一首,需定义@next事件 | ||||
| 			control: { | ||||
| 				type: Boolean, | ||||
| 				default: true | ||||
| 			}, //是否需要上一曲/下一曲按钮 | ||||
| 			color: { | ||||
| 				type: String, | ||||
| 				default: '#007BFF' | ||||
| 			} //主色调 | ||||
| 		}, | ||||
| 		methods: { | ||||
| 			//返回prev事件 | ||||
| 			prev() { | ||||
| 				this.$emit('prev') | ||||
| 			}, | ||||
| 			//返回next事件 | ||||
| 			next() { | ||||
| 				this.$emit('next') | ||||
| 			}, | ||||
| 			//格式化时长 | ||||
| 			format(num) { | ||||
| 				return '0'.repeat(2 - String(Math.floor(num / 60)).length) + Math.floor(num / 60) + ':' + '0'.repeat(2 - String(Math.floor(num % 60)).length) + Math.floor(num % 60) | ||||
| 			}, | ||||
| 			//点击播放按钮 | ||||
| 			play() { | ||||
| 				this.audio.play() | ||||
| 				this.loading = true | ||||
| 			} | ||||
| 		}, | ||||
| 		created() { | ||||
| 			if (this.src) { | ||||
| 				this.audio.src = this.src | ||||
| 				this.autoplay && this.play() | ||||
| 			} | ||||
| 			this.audio.obeyMuteSwitch = false | ||||
| 			//音频进度更新事件 | ||||
| 			this.audio.onTimeUpdate(() => { | ||||
| 				if (!this.seek) { | ||||
| 					this.current = this.audio.currentTime | ||||
| 				} | ||||
| 				if (!this.duration) { | ||||
| 					this.duration = this.audio.duration | ||||
| 				} | ||||
| 			}) | ||||
| 			//音频播放事件 | ||||
| 			this.audio.onPlay(() => { | ||||
| 				this.paused = false | ||||
| 				this.loading = false | ||||
| 			}) | ||||
| 			//音频暂停事件 | ||||
| 			this.audio.onPause(() => { | ||||
| 				this.paused = true | ||||
| 			}) | ||||
| 			//音频结束事件 | ||||
| 			this.audio.onEnded(() => { | ||||
| 				if (this.continue) { | ||||
| 					this.next() | ||||
| 				} else { | ||||
| 					this.paused = true | ||||
| 					this.current = 0 | ||||
| 				} | ||||
| 			}) | ||||
| 			//音频完成更改进度事件 | ||||
| 			this.audio.onSeeked(() => { | ||||
| 				this.seek = false | ||||
| 			}) | ||||
| 		}, | ||||
| 		beforeDestroy(){ | ||||
| 			this.audio.destroy() | ||||
| 		}, | ||||
| 		watch: { | ||||
| 			src(src, old) { | ||||
| 				this.audio.src = src | ||||
| 				this.current = 0 | ||||
| 				this.duration = 0 | ||||
| 				if (old || this.autoplay) { | ||||
| 					this.play() | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| </script> | ||||
|  | ||||
| <style> | ||||
| 	.imt-audio { | ||||
| 		background: #fff; | ||||
| 		border-radius: 20upx; | ||||
| 	} | ||||
|  | ||||
| 	.audio-wrapper { | ||||
| 		display: flex; | ||||
| 		align-items: center; | ||||
| 	} | ||||
|  | ||||
| 	.audio-number { | ||||
| 		width: 120upx; | ||||
| 		font-size: 24upx; | ||||
| 		line-height: 1; | ||||
| 		color: #999999; | ||||
| 		text-align: center; | ||||
| 	} | ||||
|  | ||||
| 	.audio-slider { | ||||
| 		flex: 1; | ||||
| 		margin: 0; | ||||
| 	} | ||||
|  | ||||
| 	.audio-control-wrapper { | ||||
| 		margin-top: 40upx; | ||||
|     text-align: center; | ||||
| 	} | ||||
|  | ||||
|   .audio-control-wrapper p { | ||||
|     color: #999999; | ||||
|     font-size: 26rpx; | ||||
|   } | ||||
|  | ||||
|   .audio-control-wrapper image { | ||||
|     width: 128rpx; | ||||
|     height: 128rpx; | ||||
|   } | ||||
|  | ||||
| 	.audio-control { | ||||
| 		font-size: 32upx; | ||||
| 		line-height: 1; | ||||
| 		border-radius: 50%; | ||||
| 	} | ||||
|  | ||||
| 	.audio-control-next { | ||||
| 		transform: rotate(180deg); | ||||
| 	} | ||||
|  | ||||
| 	.audio-control-switch { | ||||
| 		font-size: 40upx; | ||||
| 		margin: 0 100upx; | ||||
| 	} | ||||
|  | ||||
| 	.audioLoading { | ||||
| 		animation: loading 2s; | ||||
| 		animation-iteration-count: infinite; | ||||
| 		animation-timing-function: linear; | ||||
| 	} | ||||
|  | ||||
| 	@keyframes loading { | ||||
| 		to { | ||||
| 			transform: rotate(360deg); | ||||
| 		} | ||||
| 	} | ||||
| </style> | ||||
							
								
								
									
										29
									
								
								src/components/VDrag.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,29 @@ | ||||
| <template> | ||||
|   <section class="VDrag"> | ||||
|     <vuedraggable v-bind="$attrs" @change="handleChange"> | ||||
|       <slot/> | ||||
|     </vuedraggable> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import vuedraggable from 'vuedraggable' | ||||
|  | ||||
| export default { | ||||
|   name: "VDrag", | ||||
|   components: {vuedraggable}, | ||||
|   data: () => ({ | ||||
|     moveEvt: null | ||||
|   }), | ||||
|   methods: { | ||||
|     handleChange(moved) { | ||||
|       this.$emit('move', moved) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .VDrag { | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										39
									
								
								src/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,39 @@ | ||||
| import Vue from 'vue'; | ||||
| import App from './App'; | ||||
| import store from './store'; | ||||
| import axios from './common/axios'; | ||||
| import utils from './common/util'; | ||||
| import dayjs from 'dayjs'; | ||||
| import mixin from './uview/libs/mixin/mixin'; | ||||
|  | ||||
| Vue.config.productionTip = false; | ||||
| Vue.prototype.$store = store; | ||||
| //初始化接口工具类 | ||||
| axios.defaults.baseURL = store.state.baseURL; | ||||
| Vue.prototype.$http = axios; | ||||
| Vue.prototype.$cdn = 'https://cdn.cunwuyun.cn/dvcp/h5/'; | ||||
| Vue.prototype.imgHomeUrl = 'https://cdn.cunwuyun.cn/dvcp/h5/home/'; | ||||
| Vue.prototype.imgOtherUrl = 'https://cdn.cunwuyun.cn/dvcp/h5/other/'; | ||||
| Vue.prototype.$formatName = (name) => { | ||||
| 	if (name == undefined) { | ||||
| 		return; | ||||
| 	} | ||||
| 	return name.substr(name.length - 2, name.length > 2 ? name.length - 1 : name.length); | ||||
| }; | ||||
| Object.keys(utils).map((e) => (Vue.prototype['$' + e] = utils[e])); | ||||
| let relativeTime = require('dayjs/plugin/relativeTime'); | ||||
| require('dayjs/locale/zh-cn'); | ||||
| let dayjs_plugin_duration = require('dayjs/plugin/duration'); | ||||
| dayjs.extend(dayjs_plugin_duration); | ||||
| dayjs.extend(relativeTime); | ||||
| Vue.prototype.$dayjs = dayjs; | ||||
| Vue.mixin(mixin); | ||||
|  | ||||
| App.mpType = 'app'; | ||||
| process.env.NODE_ENV == 'development' && new VConsole(); | ||||
| const app = new Vue({ | ||||
| 	store, | ||||
| 	...App | ||||
| }); | ||||
|  | ||||
| app.$mount(); | ||||
							
								
								
									
										37
									
								
								src/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,37 @@ | ||||
| { | ||||
|   "name": "dv_cp_weixin", | ||||
|   "description": "企业微信应用", | ||||
|   "versionName": "1.0.0", | ||||
|   "versionCode": "100", | ||||
|   "transformPx": true, | ||||
|   "mp-weixin": { | ||||
|     "appid": "", | ||||
|     "setting": { | ||||
|       "urlCheck": false | ||||
|     }, | ||||
|     "usingComponents": true, | ||||
|     "permission": { | ||||
|       "scope.userLocation": { | ||||
|         "desc": "演示定位能力" | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "h5": { | ||||
|     "title": "数字乡村", | ||||
|     "template": "template.h5.html", | ||||
|     "router": { | ||||
|       "mode": "history", | ||||
|       "base": "" | ||||
|     }, | ||||
|     "devServer": { | ||||
|       "disableHostCheck": true, | ||||
|       "port": "10323" | ||||
|     }, | ||||
|     "optimization": { | ||||
|       "preload": true, | ||||
|       "treeShaking": { | ||||
|         "enable": true | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										411
									
								
								src/pages.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,411 @@ | ||||
| { | ||||
|   "easycom": { | ||||
|     "^u-(.*)": "@/uview/components/u-$1/u-$1.vue" | ||||
|   }, | ||||
|   "pages": [ | ||||
|     { | ||||
|       "path": "pages/loading", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "欢迎使用村微..." | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/app", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "应用" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/login" | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/notification/notification", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "通知公告" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/notification/components/read", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "接收情况" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/notification/components/detail", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "公告详情" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/notification/components/add", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "新增公告" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/workTask/workTask", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "工作任务" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/workTask/components/create", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "创建任务" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/workTask/components/detail", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "任务详情" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/workTask/components/subDetail", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "任务详情" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/workTask/components/finishDetail", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "完成详情" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/workTask/components/finish", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "完成任务" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/documentFlow/documentFlow", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "公文流转" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/documentFlow/components/detail", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "公文详情" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/documentFlow/components/approval", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "批示" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/meetingNotice/meetingNotice", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "会议通知" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/meetingNotice/components/addMeeting", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "发起会议" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/meetingNotice/components/belongToMe", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "我发起的" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/meetingNotice/components/detail", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "会议详情" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/meetingNotice/components/meetingList" | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/askForm/askForm" | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/askForm/index", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "问卷表单" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/askForm/formSetting", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "设置" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/askForm/filedConfig", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "组件设置" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/askForm/previewForm", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "表单预览" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/askForm/addForm", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "新建调查表单" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/resident/resident" | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/resident/comp", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "居民管理" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/resident/groupResident" | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/whereabouts/whereabouts" | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/interview/interview", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "调查走访" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/interview/detail", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "新增走访" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/quickReply/quickReply" | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/quickReply/typeManage" | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/quickReply/replyDetail" | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/supermarket/supermarket", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "信用好超市" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/supermarket/balance", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "结算" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/supermarket/search", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "搜索" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/supermarket/components/resultPage/resultPage", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "结算提交" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/casuallyask/casuallyask", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "随心问" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/casuallyask/casuallyaskDetail", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "随心问(详情页)" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/casuallyask/closemsg", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "关闭留言" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/casuallyask/truemsg", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "提交留言" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/casuallyask/reply", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "发表回复" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/workonline/workonline", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "网上办事" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/workonline/detail", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "进度详情" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/workonline/approvalopinion", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "进度详情" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/workonline/truemsg", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "进度详情" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/snapshot/snapshot", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "随手拍" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/snapshot/snapshotDetail", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "随手拍详情" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/snapshot/handleResult", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "处理结果" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/snapshot/components/handlePage/handlePage", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "处理结果" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/villageQRCode/villageQRCode", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "一村一码" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/guardianship/guardianship", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "智慧监护" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/guardianship/userDetail", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "个人详情" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/guardianship/historyList", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "历史记录" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/guardianship/warningDetail", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "预警" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/bigHorn/bigHorn", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "广播通知" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/bigHorn/playList", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "播放记录" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/bigHorn/onlineList", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "在线设备" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/bigHorn/onlinePlayList", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "在播设备" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/bigHorn/addPlay", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "音频播放" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/bigHorn/selectMp3", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "选择内容" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/bigHorn/selectEquipment", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "选择设备" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/resourcesManage/resourcesManage", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "媒资管理" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/resourcesManage/addPlay", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "媒资管理" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/resourcesManage/talking", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "实时喊话" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/resourcesManage/recording", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "音频录制" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/videoSurveillance/videoSurveillance", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "视频监控" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "path": "pages/videoSurveillance/monitorDetail", | ||||
|       "style": { | ||||
|         "navigationBarTitleText": "实时监控", | ||||
|         "pageOrientation": "landscape" | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "globalStyle": { | ||||
|     "pageOrientation": "auto", | ||||
|     "navigationStyle": "custom" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										174
									
								
								src/pages/app.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,174 @@ | ||||
| <template> | ||||
|   <section class="app"> | ||||
|     <u-swiper :list="list" height="240" bg-color="#408CFF"></u-swiper> | ||||
|     <label class="useful-app">常用应用</label> | ||||
|     <u-grid | ||||
|       :col="o.length" | ||||
|       :border="false" | ||||
|       v-for="(o, i) in gridGroup" | ||||
|       :key="i" | ||||
|     > | ||||
|       <u-grid-item v-for="(i, j) in o" :key="j" @click="linkTo(i)"> | ||||
|         <u-icon :name="i.icon" :size="100"></u-icon> | ||||
|         <view class="grid-text">{{ i.label }}</view> | ||||
|       </u-grid-item> | ||||
|     </u-grid> | ||||
|     <label class="useful-app notice">通知公告</label> | ||||
|     <section class="list"> | ||||
|       <div class="item" v-for="(item, index) in dataList" :key="index"> | ||||
|         <label class="title hidden">{{ item.title }}</label> | ||||
|         <div class="content hidden">{{ item.content }}</div> | ||||
|         <div class="date">{{ item.createTime }}</div> | ||||
|       </div> | ||||
|       <u-loadmore :status="status" /> | ||||
|     </section> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: 'app', | ||||
|   data() { | ||||
|     return { | ||||
|       list: [this.imgHomeUrl + 'banner.png'], | ||||
|       gridGroup: [ | ||||
|         [ | ||||
|           { | ||||
|             icon: `${this.imgHomeUrl}icon1.png`, | ||||
|             label: '随心问', | ||||
|             link: '/pages/casuallyask/casuallyask' | ||||
|           }, | ||||
|           { | ||||
|             icon: `${this.imgHomeUrl}icon2.png`, | ||||
|             label: '随手拍', | ||||
|             link: '/pages/snapshot/snapshot' | ||||
|           }, | ||||
|           { | ||||
|             icon: `${this.imgHomeUrl}icon3.png`, | ||||
|             label: '网上办事', | ||||
|             link: '/pages/workonline/workonline' | ||||
|           }, | ||||
|           { icon: `${this.imgHomeUrl}icon4.png`, label: '调查走访' } | ||||
|         ], | ||||
|         [ | ||||
|           { | ||||
|             icon: `${this.imgHomeUrl}icon5.png`, | ||||
|             label: '信用好超市', | ||||
|             link: '/pages/supermarket/supermarket' | ||||
|           }, | ||||
|           { icon: `${this.imgHomeUrl}icon6.png`, label: '中心工作' }, | ||||
|           { icon: `${this.imgHomeUrl}icon7.png`, label: '会务通知' }, | ||||
|           { icon: `${this.imgHomeUrl}icon8.png`, label: '工作去向' }, | ||||
|           { icon: `${this.imgHomeUrl}icon8.png`, label: '大喇叭' } | ||||
|         ] | ||||
|       ], | ||||
|       status: 'nomore', | ||||
|       dataList: [], | ||||
|       current: 0 | ||||
|     } | ||||
|   }, | ||||
|   onLoad() { | ||||
|     this.getList() | ||||
|   }, | ||||
|   methods: { | ||||
|     getList() { | ||||
|       if (this.loadingStatus == 'nomore') return | ||||
|       this.$http | ||||
|         .post(`/app/appworkcenternotice/list`, null, { | ||||
|           params: { | ||||
|             withoutToken: true, | ||||
|             current: this.current + 1, | ||||
|             size: 10 | ||||
|           } | ||||
|         }) | ||||
|         .then(res => { | ||||
|           if (res && res.data) { | ||||
|             this.dataList = res.data.records | ||||
|           } | ||||
|         }) | ||||
|     }, | ||||
|     linkTo(val) { | ||||
|       if (val.link) { | ||||
|         uni.navigateTo({ url: val.link }) | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   onReachBottom() { | ||||
|     this.getList() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .app { | ||||
|   min-height: 100%; | ||||
|   background-color: #ffffff; | ||||
|   box-sizing: border-box; | ||||
|   padding: 32px; | ||||
|   position: relative; | ||||
|  | ||||
|   .grid-text { | ||||
|     font-size: 28px; | ||||
|     color: #333333; | ||||
|     line-height: 46px; | ||||
|   } | ||||
|  | ||||
|   .useful-app { | ||||
|     margin-top: 80px; | ||||
|     display: inherit; | ||||
|     font-size: 36px; | ||||
|     font-weight: bold; | ||||
|     color: #333333; | ||||
|     line-height: 50px; | ||||
|   } | ||||
|  | ||||
|   .notice { | ||||
|     margin-top: 56px; | ||||
|   } | ||||
|  | ||||
|   .list { | ||||
|     margin-top: 38px; | ||||
|  | ||||
|     .item { | ||||
|       height: 192px; | ||||
|       background: #f7f9ff; | ||||
|       border-radius: 8px; | ||||
|       box-sizing: border-box; | ||||
|       padding: 20px; | ||||
|       margin-bottom: 32px; | ||||
|  | ||||
|       .title { | ||||
|         font-size: 32px; | ||||
|         font-weight: 800; | ||||
|         color: #343d65; | ||||
|       } | ||||
|  | ||||
|       .content { | ||||
|         font-size: 28px; | ||||
|         color: #333333; | ||||
|         font-weight: 500; | ||||
|         margin: 20px 0 8px 0; | ||||
|       } | ||||
|  | ||||
|       .date { | ||||
|         font-size: 28px; | ||||
|         color: #666666; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .hidden { | ||||
|     overflow: hidden; | ||||
|     text-overflow: ellipsis; | ||||
|     white-space: nowrap; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .u-indicator-item-round { | ||||
|     background-color: #d8dde6; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .u-indicator-item-round-active { | ||||
|     background-color: #408cff !important; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										700
									
								
								src/pages/askForm/addForm.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,700 @@ | ||||
| <template> | ||||
|   <div class="add-form" v-if="pageShow"> | ||||
|     <div class="header-pic"> | ||||
|       <image v-if="form.headPicture" :src="form.headPicture" /> | ||||
|       <span @click="upload">更换图片</span> | ||||
|     </div> | ||||
|     <div class="form-info"> | ||||
|       <h2>文本选项</h2> | ||||
|       <div class="form-info__wrapper"> | ||||
|         <textarea class="title" placeholder="请输入标题 (必填)" :maxlength="30" :auto-height="true" v-model="form.title"></textarea> | ||||
|         <u-input class="content" :clearable="false" type="textarea" v-model="form.tableExplain" placeholder="请输入表单描述 (选填)" :height="80" :auto-height="true" :maxlength="255"></u-input> | ||||
|       </div> | ||||
|     </div> | ||||
|     <draggable | ||||
|       class="components-list" | ||||
|       v-model="targetList" | ||||
|       :animation="340" | ||||
|       scroll | ||||
|       element="div" | ||||
|       :options="{ | ||||
|         animation: 340, | ||||
|         handle: '.components-item__title' | ||||
|       }" | ||||
|       draggable=".components-item" | ||||
|       :sort="true"> | ||||
|       <div class="components-item" v-for="(item, index) in targetList" :key="index" @click="toFiledSetting(item, index)"> | ||||
|         <div class="components-item__title"> | ||||
|           <div class="components-item__title--left"> | ||||
|             <em :style="{opacity: item.required ? 1 : 0}">*</em> | ||||
|             <i>{{ index + 1 }}.</i> | ||||
|             <h2>{{ item.label }}</h2> | ||||
|           </div> | ||||
|           <image :src="`${$cdn}askform/sc1.png`" @click.stop="removeComponent(index)" @touchstart.stop="removeComponent(index)" /> | ||||
|         </div> | ||||
|         <div class="components-item__filed"> | ||||
|           <template v-if="(item.type === 'radio')"> | ||||
|             <u-radio-group v-model="item.value" wrap> | ||||
|               <u-radio disabled :name="field.label" v-for="(field, i) in item.options" :key="i"> | ||||
|                 <image :src="field.img[0].url" v-if="field.img.length"/> | ||||
|                 <span>{{ field.label }}</span> | ||||
|               </u-radio> | ||||
|             </u-radio-group> | ||||
|           </template> | ||||
|           <template v-if="(item.type === 'checkbox')"> | ||||
|             <u-checkbox-group v-model="item.value" wrap> | ||||
|               <u-checkbox  disabled :name="field.label" v-for="(field, i) in item.options" :key="i"> | ||||
|                 <image :src="field.img[0].url" v-if="field.img.length"/> | ||||
|                 <span>{{ field.label }}</span> | ||||
|               </u-checkbox> | ||||
|             </u-checkbox-group> | ||||
|           </template> | ||||
|           <template v-if="(item.type === 'select')"> | ||||
|             <div class="components-item__select"> | ||||
|               <span>{{ item.placeholder }}</span> | ||||
|               <u-icon name="arrow-down" color="#DEDFDF" /> | ||||
|             </div> | ||||
|           </template> | ||||
|           <template v-if="(item.type === 'upload')"> | ||||
|             <div class="components-item__select components-item__textarea components-item__upload"> | ||||
|               <image :src="`${$cdn}askform/upload.png`" /> | ||||
|               <span>选择图片(2M以内)</span> | ||||
|             </div> | ||||
|           </template> | ||||
|           <template v-if="(item.type === 'input')"> | ||||
|             <div class="components-item__select"> | ||||
|               <span>{{ item.placeholder }}</span> | ||||
|             </div> | ||||
|           </template> | ||||
|           <template v-if="(item.type === 'textarea')"> | ||||
|             <div class="components-item__select components-item__textarea"> | ||||
|               <span>{{ item.placeholder }}</span> | ||||
|             </div> | ||||
|           </template> | ||||
|         </div> | ||||
|       </div> | ||||
|     </draggable> | ||||
|     <div class="add-form__btn" @click="isShow = true"> | ||||
|       <image :src="`${$cdn}askform/add.png`" /> | ||||
|       <span>添加问题</span> | ||||
|     </div> | ||||
|     <div class="add-form__footer"> | ||||
|       <div> | ||||
|         <span @click="toPreview">预览</span> | ||||
|         <span @click="toSetting">设置</span> | ||||
|       </div> | ||||
|       <div @click="onConfirm">立即发布</div> | ||||
|     </div> | ||||
|     <u-popup v-model="isShow" :closeable="false" mode="bottom"> | ||||
|       <div class="add-popup"> | ||||
|         <div class="add-popup__title"> | ||||
|           <h2>添加问题</h2> | ||||
|           <image :src="`${$cdn}askform/zk.png`" mode="aspectFit" @click="isShow = false" /> | ||||
|         </div> | ||||
|         <div class="add-popup__list"> | ||||
|           <span @click="toFiledSetting('radio')">单选题</span> | ||||
|           <span @click="toFiledSetting('checkbox')">多选题</span> | ||||
|           <span @click="toFiledSetting('select')">单下拉框</span> | ||||
|           <span @click="toFiledSetting('input')">单行填空</span> | ||||
|           <span @click="toFiledSetting('textarea')">多行填空</span> | ||||
|           <span @click="toFiledSetting('upload')">上传图片</span> | ||||
|         </div> | ||||
|       </div> | ||||
|     </u-popup> | ||||
|     <AiBack></AiBack> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import AiBack from "@/components/AiBack"; | ||||
|   import draggable from 'vuedraggable' | ||||
|   export default { | ||||
|     data () { | ||||
|       return { | ||||
|         pageShow: false, | ||||
|         form: { | ||||
|           tableExplain: '详细描述', | ||||
|           title: '问卷调查', | ||||
|           isShowheadPicture: true, | ||||
|           isShowTableExplain: true, | ||||
|           isShowBtn: true, | ||||
|           headPicture: '', | ||||
|           commitType: '1', | ||||
|           periodValidityType: '0', | ||||
|           actionNotice: '1', | ||||
|           dynamicNotice: '1', | ||||
|           periodValidityEndTime: '', | ||||
|           shareStatus: '0', | ||||
|           count: 0, | ||||
|           wechatId: '0', | ||||
|           type: 0, | ||||
|           buttonExplain: '提交', | ||||
|           tips: true | ||||
|         }, | ||||
|         templateType: 0, | ||||
|         targetList: [], | ||||
|         isShow: false, | ||||
|         type: 0, | ||||
|         id: '', | ||||
|         isQuote: false, | ||||
|         touchStart: 0, | ||||
|         formConfig: {} | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     components: { | ||||
|       AiBack, | ||||
|       draggable | ||||
|     }, | ||||
|  | ||||
|     onLoad (query) { | ||||
|       this.type = Number(query.type) | ||||
|  | ||||
|       if (query.isQuote) { | ||||
|         this.isQuote = true | ||||
|       } | ||||
|  | ||||
|       if (query.id) { | ||||
|         this.id = query.id | ||||
|         this.getInfo(query.id) | ||||
|       } else { | ||||
|         this.pageShow = true | ||||
|       } | ||||
|       this.init() | ||||
|  | ||||
|       uni.$on('setting', res => { | ||||
|         this.form = { | ||||
|           ...this.form, | ||||
|           ...res | ||||
|         } | ||||
|         this.formConfig = res | ||||
|       }) | ||||
|  | ||||
|       uni.$on('filedConfig', res => { | ||||
|         if (res.index === '-1') { | ||||
|           this.targetList.push(res.config) | ||||
|         } else { | ||||
|           this.$set(this.targetList, [res.index], res.config) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       toSetting () { | ||||
|         uni.navigateTo({ | ||||
|           url: `/pages/askForm/formSetting?id=${this.id}&formConfig=${JSON.stringify(this.formConfig)}` | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       removeComponent (index) { | ||||
|         this.targetList.splice(index, 1) | ||||
|       }, | ||||
|  | ||||
|       toPreview () { | ||||
|         uni.navigateTo({ | ||||
|           url: `/pages/askForm/previewForm?targetList=${JSON.stringify(this.targetList)}&form=${JSON.stringify(this.form)}` | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       upload() { | ||||
|         let params = { | ||||
|           count: 1, | ||||
|           sizeType: ['compressed'], | ||||
|           sourceType: ['album', 'camera'], | ||||
|           success: (res) => { | ||||
|             let count = this.fileList?.length + (res.tempFiles?.length || res.tempFile ? 1 : 0) | ||||
|             if (count > 1) { | ||||
|               return this.$u.toast(`不能超过1个`) | ||||
|             } | ||||
|             if (res.tempFiles) { | ||||
|               res.tempFiles.map((item) => { | ||||
|                 this.uploadFile(item) | ||||
|               }) | ||||
|             } else if (res?.tempFile) { | ||||
|               this.uploadFile(res.tempFile) | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         uni.chooseImage(params) | ||||
|       }, | ||||
|  | ||||
|       uploadFile (img) { | ||||
|         uni.showLoading({title: '上传中'}) | ||||
|         let formData = new FormData() | ||||
|         formData.append('file', img) | ||||
|         this.$http.post('/admin/file/add2', formData).then((res) => { | ||||
|           uni.hideLoading() | ||||
|           if (res?.data) { | ||||
|             this.$u.toast('上传成功!') | ||||
|             this.form.headPicture = res.data.url | ||||
|           } | ||||
|         }).catch(res => { | ||||
|           this.$u.toast(res) | ||||
|           uni.hideLoading() | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       onConfirm () { | ||||
|         for (let item of this.targetList) { | ||||
|           if (item.isShowPoints) { | ||||
|             if (item.pointType === '0') { | ||||
|               if (!item.answer || JSON.stringify(item.answer) === '[]') { | ||||
|                 return this.$u.toast(`请输入${item.label}正确答案`) | ||||
|               } | ||||
|  | ||||
|               if (!item.points) { | ||||
|                 return this.$u.toast(`请输入${item.label}的分值`) | ||||
|               } | ||||
|             } | ||||
|  | ||||
|             if (item.pointType === '1') { | ||||
|               for (let option of item.options) { | ||||
|                 if (!option.point) { | ||||
|                   return this.$u.toast(`请输入${item.label}${option.label}的分值`) | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|  | ||||
|             if (item.pointType === '2') { | ||||
|               for (let option of item.options) { | ||||
|                 if (!option.point) { | ||||
|                   return this.$u.toast(`请输入${item.label}${option.label}的分值`) | ||||
|                 } | ||||
|               } | ||||
|  | ||||
|               if (!item.points) { | ||||
|                 return this.$u.toast(`请输入${item.label}全部答对分值`) | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         const fields = this.targetList.map(item => { | ||||
|           return { | ||||
|             fieldType: item.type, | ||||
|             fieldName: item.label, | ||||
|             fieldInfo: JSON.stringify(item) | ||||
|           } | ||||
|         }) | ||||
|  | ||||
|         this.$http.post(`/app/appquestionnairetemplate/addOrUpdate`, { | ||||
|           ...this.form, | ||||
|           fields, | ||||
|           status: 1, | ||||
|           id: this.isQuote ? '' : this.id, | ||||
|           headPicture: this.form.headPicture, | ||||
|           type: this.type, | ||||
|           templateType: 0 | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.$u.toast('提交成功') | ||||
|  | ||||
|             setTimeout(() => { | ||||
|               uni.$emit('reload') | ||||
|               uni.navigateBack({ | ||||
|                 delta: 1 | ||||
|               }) | ||||
|             }, 600) | ||||
|           } | ||||
|         }).catch(e => { | ||||
|           this.$u.toast(e) | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       getInfo (id) { | ||||
|         uni.showLoading() | ||||
|         this.$http.post(`/app/appquestionnairetemplate/queryDetailById?id=${id}`).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.form = { | ||||
|               ...res.data, | ||||
|               headPicture: res.data.headPicture | ||||
|             } | ||||
|  | ||||
|             this.type = res.data.type | ||||
|  | ||||
|             this.targetList = res.data.fields.map(item => { | ||||
|               return JSON.parse(item.fieldInfo) | ||||
|             }) | ||||
|  | ||||
|             this.pageShow = true | ||||
|           } else { | ||||
|             this.$u.toast(res.msg) | ||||
|           } | ||||
|  | ||||
|           uni.hideLoading() | ||||
|         }).catch(() => { | ||||
|           uni.hideLoading() | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       toFiledSetting (type, index) { | ||||
|         if (index != undefined) { | ||||
|           uni.navigateTo({ | ||||
|             url: `/pages/askForm/filedConfig?config=${JSON.stringify(type)}&index=${index}` | ||||
|           }) | ||||
|  | ||||
|           return false | ||||
|         } | ||||
|  | ||||
|         this.isShow = false | ||||
|         uni.navigateTo({ | ||||
|           url: `/pages/askForm/filedConfig?type=${type}` | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       init () { | ||||
|         if (this.type == 0) { | ||||
|           this.form.headPicture = 'https://cdn.cunwuyun.cn/dvcp/h5/form/interview.png' | ||||
|         } | ||||
|         if (this.type == 1) { | ||||
|           this.form.title = '考试测评' | ||||
|           this.form.headPicture = 'https://cdn.cunwuyun.cn/dvcp/h5/form/exam.png' | ||||
|         } | ||||
|         if (this.type == 2) { | ||||
|           this.form.title = '报名登记' | ||||
|           this.form.headPicture = 'https://cdn.cunwuyun.cn/dvcp/h5/form/apply.png' | ||||
|         } | ||||
|         if (this.type == 3) { | ||||
|           this.form.title = '满意调查' | ||||
|           this.form.headPicture = 'https://cdn.cunwuyun.cn/dvcp/h5/form/satisfaction.png' | ||||
|         } | ||||
|         if (this.type == 4) { | ||||
|           this.form.title = '投票评选' | ||||
|           this.form.headPicture = 'https://cdn.cunwuyun.cn/dvcp/h5/form/vote.png' | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .add-form { | ||||
|     min-height: 100vh; | ||||
|     padding-bottom: 140px; | ||||
|     box-sizing: border-box; | ||||
|     background: #F3F6F9; | ||||
|  | ||||
|     * { | ||||
|       box-sizing: border-box; | ||||
|     } | ||||
|  | ||||
|     ::v-deep .u-drawer-bottom { | ||||
|       background-color: transparent; | ||||
|     } | ||||
|  | ||||
|     .components-list { | ||||
|       padding: 0 20px; | ||||
|  | ||||
|       .components-item { | ||||
|         margin-top: 24px; | ||||
|         padding: 32px; | ||||
|         box-shadow: 0 4px 8px 4px rgba(233, 233, 233, 0.39); | ||||
|         border-radius: 8px; | ||||
|         overflow: hidden; | ||||
|         border: 1px solid #EEEFF0; | ||||
|         background: #fff; | ||||
|  | ||||
|         ::v-deep .u-radio, ::v-deep .u-checkbox { | ||||
|           position: relative; | ||||
|           margin-bottom: 20px; | ||||
|  | ||||
|           &:last-child { | ||||
|             margin-bottom: 0; | ||||
|           } | ||||
|  | ||||
|           .u-checkbox__icon-wrap, .u-radio__icon-wrap { | ||||
|             position: absolute; | ||||
|             top: 4rpx; | ||||
|           } | ||||
|  | ||||
|           .u-radio__label, .u-checkbox__label { | ||||
|             display: flex; | ||||
|             align-items: center; | ||||
|             margin-right: 0; | ||||
|             margin-left: 40px; | ||||
|             text-align: justify; | ||||
|  | ||||
|             span { | ||||
|               line-height: 1.5; | ||||
|             } | ||||
|  | ||||
|             image { | ||||
|               width: 100px; | ||||
|               height: 100px; | ||||
|               margin: 0 10px; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         span { | ||||
|           flex: 1; | ||||
|           color: #666; | ||||
|           font-size: 26px; | ||||
|         } | ||||
|  | ||||
|         .components-item__select { | ||||
|           display: flex; | ||||
|           align-items: center; | ||||
|           justify-content: space-between; | ||||
|           width: 100%; | ||||
|           height: 80px; | ||||
|           margin-bottom: 8px; | ||||
|           padding: 0 26px; | ||||
|           border: 1px solid #DEDFDF; | ||||
|  | ||||
|           &.components-item__textarea { | ||||
|             align-items: flex-start; | ||||
|             height: 160px; | ||||
|             padding-top: 20px; | ||||
|  | ||||
|             image { | ||||
|               width: 46px; | ||||
|               height: 34px; | ||||
|               margin-right: 16px; | ||||
|             } | ||||
|  | ||||
|             span { | ||||
|               color: #666; | ||||
|               font-size: 26px; | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           &.components-item__upload { | ||||
|             justify-content: center; | ||||
|             align-items: center; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         .components-item__title { | ||||
|           display: flex; | ||||
|           align-items: center; | ||||
|           justify-content: space-between; | ||||
|           margin-bottom: 32px; | ||||
|  | ||||
|           em { | ||||
|             margin-right: 4px; | ||||
|             font-style: normal; | ||||
|             color: rgb(226, 33, 32);; | ||||
|           } | ||||
|  | ||||
|           image { | ||||
|             position: relative; | ||||
|             flex-shrink: 1; | ||||
|             right: -20px; | ||||
|             width: 32px; | ||||
|             height: 32px; | ||||
|             box-sizing: content-box; | ||||
|             padding: 30px 20px 30px 20px; | ||||
|           } | ||||
|  | ||||
|           div { | ||||
|             display: flex; | ||||
|             align-items: baseline; | ||||
|             max-width: 550px; | ||||
|             color: #333333; | ||||
|             font-size: 32px; | ||||
|  | ||||
|             i { | ||||
|               font-style: normal; | ||||
|             } | ||||
|  | ||||
|             h2 { | ||||
|               font-weight: 600; | ||||
|               font-size: 32px; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .add-popup { | ||||
|       height: 440px; | ||||
|       border-radius: 20px 20px 0 0; | ||||
|       background: #fff; | ||||
|  | ||||
|       .add-popup__title { | ||||
|         display: flex; | ||||
|         position: relative; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         height: 96px; | ||||
|         border-bottom: 1px solid #E4E5E6; | ||||
|  | ||||
|         h2 { | ||||
|           color: #333333; | ||||
|           font-size: 32px; | ||||
|           font-weight: 600; | ||||
|         } | ||||
|  | ||||
|         image { | ||||
|           position: absolute; | ||||
|           right: 32px; | ||||
|           top: 50%; | ||||
|           width: 30px; | ||||
|           height: 20px; | ||||
|           transform: translateY(-50%); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .add-popup__list { | ||||
|         display: flex; | ||||
|         flex-wrap: wrap; | ||||
|         padding: 0 34px; | ||||
|  | ||||
|         span { | ||||
|           width: calc((100% - 64px) / 3); | ||||
|           height: 78px; | ||||
|           line-height: 78px; | ||||
|           margin-top: 32px; | ||||
|           margin-right: 32px; | ||||
|           text-align: center; | ||||
|           color: #333333; | ||||
|           font-size: 28px; | ||||
|           border-radius: 8px; | ||||
|           border: 1px solid #E4E5E6; | ||||
|  | ||||
|           &:nth-of-type(3n) { | ||||
|             margin-right: 0; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .add-form__footer { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       position: fixed; | ||||
|       left: 0; | ||||
|       bottom: 0; | ||||
|       z-index: 1; | ||||
|       width: 100%; | ||||
|       height: 112px; | ||||
|       text-align: center; | ||||
|  | ||||
|       div { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         flex: 1; | ||||
|         height: 100%; | ||||
|         text-align: center; | ||||
|         background: #fff; | ||||
|  | ||||
|         &:last-child { | ||||
|           color: #fff; | ||||
|           font-size: 36px; | ||||
|           background: #3192F4; | ||||
|  | ||||
|           &:active { | ||||
|             opacity: 0.8; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         span { | ||||
|           flex: 1; | ||||
|           height: 100%; | ||||
|           line-height: 112px; | ||||
|           color: #333333; | ||||
|           font-size: 32px; | ||||
|  | ||||
|           &:active { | ||||
|             background: #eee; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .add-form__btn { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       justify-content: center; | ||||
|       width: 214px; | ||||
|       height: 66px; | ||||
|       line-height: 66px; | ||||
|       margin: 64px auto 0; | ||||
|       background: #FFFFFF; | ||||
|       border-radius: 34px; | ||||
|  | ||||
|       &:active { | ||||
|         opacity: 0.8; | ||||
|       } | ||||
|  | ||||
|       image { | ||||
|         width: 28px; | ||||
|         height: 28px; | ||||
|       } | ||||
|  | ||||
|       span { | ||||
|         margin-left: 16px; | ||||
|         color: #4392E6; | ||||
|         font-size: 28px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     * { | ||||
|       box-sizing: border-box; | ||||
|     } | ||||
|  | ||||
|     .form-info { | ||||
|       padding: 0 20px; | ||||
|  | ||||
|       & > h2 { | ||||
|         height: 76px; | ||||
|         line-height: 76px; | ||||
|         color: #999999; | ||||
|         font-weight: normal; | ||||
|         font-size: 28px; | ||||
|       } | ||||
|  | ||||
|       .form-info__wrapper { | ||||
|         padding: 0 18px; | ||||
|         background: #fff; | ||||
|  | ||||
|         .title { | ||||
|           width: 100%; | ||||
|           padding: 22px 0; | ||||
|           font-size: 36px; | ||||
|           border-bottom: 1px solid #F1F2F3; | ||||
|         } | ||||
|  | ||||
|         .content { | ||||
|           padding: 30px 0!important; | ||||
|           font-size: 28px; | ||||
|  | ||||
|           ::v-deep textarea { | ||||
|             color: #333; | ||||
|             font-size: 28px!important; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .header-pic { | ||||
|       position: relative; | ||||
|       font-size: 0; | ||||
|  | ||||
|       span { | ||||
|         position: absolute; | ||||
|         bottom: 16px; | ||||
|         right: 16px; | ||||
|         z-index: 1; | ||||
|         width: 148px; | ||||
|         height: 56px; | ||||
|         line-height: 56px; | ||||
|         text-align: center; | ||||
|         color: #fff; | ||||
|         font-size: 26px; | ||||
|         background: rgba(0, 0, 0, 0.16); | ||||
|         border-radius: 28px; | ||||
|       } | ||||
|  | ||||
|       image { | ||||
|         width: 100%; | ||||
|         height: 320px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     ::v-deep .u-radio, ::v-deep .u-checkbox { | ||||
|       align-items: baseline; | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
							
								
								
									
										52
									
								
								src/pages/askForm/askForm.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,52 @@ | ||||
| <template> | ||||
|   <section class="askForm"> | ||||
|     <template v-if="showDetail&&!isManager"> | ||||
|       <form-detail/> | ||||
|     </template> | ||||
|     <template v-else-if="isManager"> | ||||
|       <form-list ref="FormList"/> | ||||
|     </template> | ||||
|     <ai-loading v-else :tips="errMsg"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import AiLoading from "../../components/AiLoading"; | ||||
| import {mapState} from "vuex"; | ||||
| import FormDetail from "./formDetail"; | ||||
| import FormList from "./formList"; | ||||
|  | ||||
| export default { | ||||
|   name: "askForm", | ||||
|   components: {FormList, FormDetail, AiLoading}, | ||||
|   computed: { | ||||
|     ...mapState(['openUser', 'user']), | ||||
|     showDetail() { | ||||
|       return !!this.$route.query?.id | ||||
|     }, | ||||
|     isManager() { | ||||
|       let {hash, query: {preview}} = this.$route | ||||
|       if (preview) return false | ||||
|       else if (hash == "#dev") return true | ||||
|       else return hash != '#form' && !!this.user.id | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       errMsg: "加载表单中..." | ||||
|     } | ||||
|   }, | ||||
|   onReachBottom() { | ||||
|     this.$refs?.FormList?.reachBottom() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .askForm { | ||||
|   position: absolute; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   background: #fff; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										182
									
								
								src/pages/askForm/components/addList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,182 @@ | ||||
| <template> | ||||
|   <div class="form"> | ||||
|     <div class="form-list"> | ||||
|       <div | ||||
|         class="form-list__item" | ||||
|         @click="toAdd(`/pages/askForm/addForm?type=${index}`)" | ||||
|         :style="{'background-image': `url(${$cdn}askform/${index + 1}.png)`}" | ||||
|         v-for="(item, index) in itemList" | ||||
|         :key="index"> | ||||
|         <h2>{{ item.name }}</h2> | ||||
|         <div>立即创建</div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="template" v-if="list.length"> | ||||
|       <h2>共享模板</h2> | ||||
|       <div class="template-list"> | ||||
|         <div class="template-item" v-for="(item, index) in list" :key="index" hover-class="bg-hover" @click="quote(item.id)"> | ||||
|           <image :src="`${$cdn}askform/6.png`" /> | ||||
|           <h2>{{ item.title }}</h2> | ||||
|           <u-icon name="arrow-right" color="#E1E2E3" /> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: 'addList', | ||||
|   label: '新建项目', | ||||
|  | ||||
|   data () { | ||||
|     return { | ||||
|       itemList: [{ | ||||
|         name: '调查问卷' | ||||
|       }, { | ||||
|         name: '考试测评' | ||||
|       }, { | ||||
|         name: '报名登记' | ||||
|       }, { | ||||
|         name: '满意调查' | ||||
|       }, { | ||||
|         name: '投票评选' | ||||
|       }], | ||||
|       list: [] | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   mounted () { | ||||
|     this.getList() | ||||
|   }, | ||||
|  | ||||
|   methods: { | ||||
|     toAdd (url) { | ||||
|       uni.navigateTo({ | ||||
|         url | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     quote (id) { | ||||
|       uni.navigateTo({ | ||||
|         url: `/pages/askForm/addForm?isQuote=1&id=${id}` | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     getList () { | ||||
|       this.$http.post(`/app/appquestionnairetemplate/list`, null, { | ||||
|         params: { | ||||
|           current: 1, | ||||
|           templateType: 1, | ||||
|           size: 10000 | ||||
|         } | ||||
|       }).then(res => { | ||||
|         if (res.code == 0) { | ||||
|           this.list = res.data.records | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .template { | ||||
|     margin: 32px 32px 0; | ||||
|     background: #fff; | ||||
|     border-radius: 8px; | ||||
|     overflow: hidden; | ||||
|  | ||||
|     & > h2 { | ||||
|       height: 88px; | ||||
|       line-height: 88px; | ||||
|       padding: 0 24px; | ||||
|       color: #333333; | ||||
|       font-size: 30px; | ||||
|     } | ||||
|  | ||||
|     .template-item { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       height: 104px; | ||||
|       padding: 0 24px; | ||||
|       border-bottom: 1px solid #D8DDE6; | ||||
|  | ||||
|       &:active { | ||||
|         background-color: #eee; | ||||
|       } | ||||
|  | ||||
|       &:last-child { | ||||
|         border: none; | ||||
|       } | ||||
|  | ||||
|       image { | ||||
|         width: 36px; | ||||
|         height: 42px; | ||||
|       } | ||||
|  | ||||
|       i { | ||||
|         font-size: 30px; | ||||
|         color: #E1E2E3; | ||||
|       } | ||||
|  | ||||
|       h2 { | ||||
|         flex: 1; | ||||
|         padding: 0 18px; | ||||
|         color: #333333; | ||||
|         font-size: 28px; | ||||
|         font-weight: normal; | ||||
|         overflow: hidden; | ||||
|         text-overflow:ellipsis; | ||||
|         white-space: nowrap; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .form-list { | ||||
|     display: flex; | ||||
|     flex-wrap: wrap; | ||||
|     padding: 0 32px 0; | ||||
|  | ||||
|     div { | ||||
|       box-sizing: border-box; | ||||
|     } | ||||
|  | ||||
|     .form-list__item { | ||||
|       width: calc(50% - 13px); | ||||
|       height: 216px; | ||||
|       margin: 32px 24px 0 0; | ||||
|       padding: 40px 20px 52px; | ||||
|       background-color: #FFFFFF; | ||||
|       border-radius: 8px; | ||||
|       background-size: 100% 100%; | ||||
|  | ||||
|       &:active { | ||||
|         background-color: #eee; | ||||
|       } | ||||
|  | ||||
|       &:nth-of-type(2n) { | ||||
|         margin-right: 0; | ||||
|       } | ||||
|  | ||||
|       div { | ||||
|         width: 148px; | ||||
|         height: 48px; | ||||
|         line-height: 48px; | ||||
|         text-align: center; | ||||
|         color: #fff; | ||||
|         font-size: 28px; | ||||
|         background: #6BA1F9; | ||||
|         border-radius: 24px; | ||||
|       } | ||||
|  | ||||
|       h2 { | ||||
|         margin-bottom: 32px; | ||||
|         padding-left: 10px; | ||||
|         color: #333333; | ||||
|         font-weight: 700; | ||||
|         font-size: 32px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
							
								
								
									
										497
									
								
								src/pages/askForm/components/list.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,497 @@ | ||||
| <template> | ||||
|   <div class="form"> | ||||
|     <ai-top-fixed> | ||||
|       <u-search placeholder="请输入标题" :show-action="false" search-icon-color="#ccc" v-model="search.title" | ||||
|                 @search="isMore = false, search.current = 1, getList()"/> | ||||
|     </ai-top-fixed> | ||||
|     <div class="form-list"> | ||||
|       <div class="form-item" v-for="(item, index) in list" :key="index" | ||||
|            @click="info = item, id = item.id, isShow = true"> | ||||
|         <div class="form-item__top"> | ||||
|           <div class="form-item__left"> | ||||
|             <h2>{{ item.title }}</h2> | ||||
|             <div class="form-item__left--info"> | ||||
|               <span>{{ item.createUserName }}</span> | ||||
|               <span>{{ item.createUnitName }}</span> | ||||
|               <span>{{ item.createTime.substr(0, item.createTime.length - 3) }}</span> | ||||
|               <span>{{ $dict.getLabel('questionnaireType', item.type) }}</span> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div class="form-item__right"> | ||||
|             <h2>{{ item.dataCount }}</h2> | ||||
|             <span>答卷数量</span> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="form-item__bottom form-item__bottom--active"> | ||||
|           <i :style="{background: $dict.getColor('questionnaireStatus', item.status)}"></i> | ||||
|           <span>{{ $dict.getLabel('questionnaireStatus', item.status) }}</span> | ||||
|         </div> | ||||
|       </div> | ||||
|       <ai-empty v-if="!list.length && isMore"></ai-empty> | ||||
|     </div> | ||||
|     <u-popup v-model="isShow" :closeable="false" mode="bottom" :z-index="11"> | ||||
|       <div class="popup"> | ||||
|         <h2>{{ info.title }}</h2> | ||||
|         <div class="operate-list"> | ||||
|           <div class="operate-item" @click="toEdit"> | ||||
|             <div> | ||||
|               <image :src="`${$cdn}askform/bj.png`"/> | ||||
|             </div> | ||||
|             <h3>编辑</h3> | ||||
|           </div> | ||||
|           <div class="operate-item" @click="linkTo('/pages/askForm/askForm?preview=1&id=' + id)"> | ||||
|             <div> | ||||
|               <image :src="`${$cdn}askform/yl.png`"/> | ||||
|             </div> | ||||
|             <h3>预览</h3> | ||||
|           </div> | ||||
|           <div class="operate-item" @click="publish" v-if="info.status !== '1'"> | ||||
|             <div> | ||||
|               <image :src="`${$cdn}askform/fb.png`"/> | ||||
|             </div> | ||||
|             <h3>发布</h3> | ||||
|           </div> | ||||
|           <div class="operate-item" @click="toStop" v-if="info.status === '1'"> | ||||
|             <div> | ||||
|               <image :src="`${$cdn}askform/stop.png`"/> | ||||
|             </div> | ||||
|             <h3>停止</h3> | ||||
|           </div> | ||||
|           <div class="operate-item" @click="showShare"> | ||||
|             <div> | ||||
|               <image :src="`${$cdn}askform/fx.png`"/> | ||||
|             </div> | ||||
|             <h3>分享</h3> | ||||
|           </div> | ||||
|           <div class="operate-item" @click="share(id)"> | ||||
|             <div> | ||||
|               <image :src="`${$cdn}askform/mb.png`"/> | ||||
|             </div> | ||||
|             <h3>共享为模板</h3> | ||||
|           </div> | ||||
|           <div class="operate-item" @click="remove(id)"> | ||||
|             <div> | ||||
|               <image :src="`${$cdn}askform/sc.png`"/> | ||||
|             </div> | ||||
|             <h3>删除</h3> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="popup-btn" @click="isShow = false">关闭</div> | ||||
|       </div> | ||||
|     </u-popup> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import AiTopFixed from '@/components/AiTopFixed' | ||||
| import AiEmpty from '@/components/AiEmpty/AiEmpty' | ||||
| import {mapActions} from 'vuex' | ||||
|  | ||||
| export default { | ||||
|   name: 'formList', | ||||
|   label: '表单列表', | ||||
|  | ||||
|   data() { | ||||
|     return { | ||||
|       search: { | ||||
|         current: 1, | ||||
|         templateType: 0, | ||||
|         title: '' | ||||
|       }, | ||||
|       id: '', | ||||
|       info: {}, | ||||
|       isMore: false, | ||||
|       list: [], | ||||
|       isShow: false | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   components: { | ||||
|     AiEmpty, | ||||
|     AiTopFixed | ||||
|   }, | ||||
|  | ||||
|   mounted() { | ||||
|     this.injectJWeixin(['sendChatMessage', 'selectEnterpriseContact']) | ||||
|     this.$dict.load(['questionnaireStatus', 'questionnaireType', 'questionnaireFieldType']).then(() => { | ||||
|       this.getList() | ||||
|     }) | ||||
|   }, | ||||
|  | ||||
|   methods: { | ||||
|     ...mapActions(['injectJWeixin', 'wxInvoke']), | ||||
|  | ||||
|     linkTo(url) { | ||||
|       this.isShow = false | ||||
|  | ||||
|       uni.navigateTo({ | ||||
|         url | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     toStop() { | ||||
|       this.$http.post(`/app/appquestionnairetemplate/stopRelease?id=${this.info.id}`).then(res => { | ||||
|         if (res.code === 0) { | ||||
|           this.$u.toast('停止成功') | ||||
|           this.search.current = 1 | ||||
|           this.isShow = false | ||||
|           this.isMore = false | ||||
|           this.getList() | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     showShare() { | ||||
|       if (this.info.status !== '1') { | ||||
|         this.isShow = false | ||||
|         return this.$u.toast(`该表单${this.info.status === '0' ? '未发布' : '已截止'},无法分享!`) | ||||
|       } | ||||
|  | ||||
|       uni.showActionSheet({ | ||||
|         itemList: ['分享', '微信分享', '获取链接'], | ||||
|         success: data => { | ||||
|           this.$http.post(`/app/appquestionnairetemplate/queryQrCode?id=${this.info.id}`).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               if (data.tapIndex === 2) { | ||||
|                 this.copy(res.data.linkUrl) | ||||
|                 this.isShow = false | ||||
|               } | ||||
|               if (data.tapIndex === 0 || data.tapIndex === 1) { | ||||
|                 this.injectJWeixin(['shareAppMessage', 'shareWechatMessage']).then(() => { | ||||
|                   if (data.tapIndex === 0) { | ||||
|                     this.wxInvoke(['shareAppMessage', { | ||||
|                       title: this.info.title, | ||||
|                       desc: this.info.tableExplain, | ||||
|                       link: res.data.linkUrl, | ||||
|                       imgUrl: this.info.headPicture | ||||
|                     }, () => { | ||||
|                       this.isShow = false | ||||
|                     }]) | ||||
|                   } else { | ||||
|                     this.wxInvoke(['shareWechatMessage', { | ||||
|                       title: this.info.title, | ||||
|                       desc: this.info.tableExplain, | ||||
|                       link: res.data.linkUrl, | ||||
|                       imgUrl: this.info.headPicture | ||||
|                     }, () => { | ||||
|                       this.isShow = false | ||||
|                     }]) | ||||
|                   } | ||||
|                 }) | ||||
|               } | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     copy(link) { | ||||
|       let oInput = document.createElement('input') | ||||
|       oInput.value = link | ||||
|       document.body.appendChild(oInput) | ||||
|       oInput.select() | ||||
|       document.execCommand('Copy') | ||||
|       this.$u.toast('已复制') | ||||
|       oInput.remove() | ||||
|     }, | ||||
|  | ||||
|     publish() { | ||||
|       if (this.info.status === '1') { | ||||
|         return this.$u.toast('该表单已发布') | ||||
|       } | ||||
|  | ||||
|       this.linkTo(`/pages/askForm/formSetting?id=${this.info.id}&type=edit`) | ||||
|       this.isShow = false | ||||
|     }, | ||||
|  | ||||
|     toEdit() { | ||||
|       if (this.info.dataCount !== 0) { | ||||
|         return this.$u.toast('该表单已有数据,无法编辑!') | ||||
|       } | ||||
|  | ||||
|       uni.navigateTo({ | ||||
|         url: `/pages/askForm/addForm?id=${this.id}` | ||||
|       }) | ||||
|  | ||||
|       this.isShow = false | ||||
|     }, | ||||
|  | ||||
|     share(id) { | ||||
|       this.$http.post(`/app/appquestionnairetemplate/share?id=${id}`).then(res => { | ||||
|         if (res.code === 0) { | ||||
|           this.$confirm('调查表单共享成功,其他成员可在新建项目时直接使用!', '', { | ||||
|             showCancel: false | ||||
|           }) | ||||
|           this.isShow = false | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     remove(id) { | ||||
|       if (this.info.dataCount !== 0) { | ||||
|         return this.$u.toast('该表单已有数据,无法删除!') | ||||
|       } | ||||
|  | ||||
|       this.$confirm('确定删除该数据?').then(() => { | ||||
|         this.$http.post(`/app/appquestionnairetemplate/delete?id=${id}`).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.$u.toast('删除成功') | ||||
|             this.isShow = false | ||||
|             this.search.current = 1 | ||||
|             this.isMore = false | ||||
|  | ||||
|             this.getList() | ||||
|           } | ||||
|         }) | ||||
|       }).catch(() => { | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     reload() { | ||||
|       this.isMore = false | ||||
|       this.search.current = 1 | ||||
|  | ||||
|       this.getList() | ||||
|     }, | ||||
|  | ||||
|     getList() { | ||||
|       if (this.isMore) return | ||||
|  | ||||
|       this.$http.post(`/app/appquestionnairetemplate/list`, null, { | ||||
|         params: { | ||||
|           ...this.search, | ||||
|           size: 10 | ||||
|         } | ||||
|       }).then(res => { | ||||
|         if (res.code == 0) { | ||||
|           if (this.search.current > 1) { | ||||
|             this.list = [...this.list, ...res.data.records] | ||||
|           } else { | ||||
|             this.list = res.data.records | ||||
|           } | ||||
|  | ||||
|           uni.hideLoading() | ||||
|  | ||||
|           if (res.data.records.length < 10) { | ||||
|             this.isMore = true | ||||
|  | ||||
|             return false | ||||
|           } | ||||
|  | ||||
|           this.search.current = this.search.current + 1 | ||||
|         } else { | ||||
|           uni.hideLoading() | ||||
|         } | ||||
|       }).catch(() => { | ||||
|         uni.hideLoading() | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .form { | ||||
|   ::v-deep .u-search { | ||||
|     margin-bottom: 0 !important; | ||||
|   } | ||||
|  | ||||
|   .popup { | ||||
|     background: #F7F7F7; | ||||
|  | ||||
|     & > h2 { | ||||
|       height: 72px; | ||||
|       line-height: 72px; | ||||
|       padding: 0 20px; | ||||
|       color: #999999; | ||||
|       font-size: 22px; | ||||
|       text-align: center; | ||||
|       border-bottom: 2px solid #D7D8DA; | ||||
|       overflow: hidden; | ||||
|       text-overflow: ellipsis; | ||||
|       white-space: nowrap; | ||||
|     } | ||||
|  | ||||
|     .popup-btn { | ||||
|       height: 96px; | ||||
|       line-height: 96px; | ||||
|       text-align: center; | ||||
|       color: #333333; | ||||
|       font-size: 30px; | ||||
|       background: #fff; | ||||
|  | ||||
|       &:active { | ||||
|         background: #eee; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .operate-list { | ||||
|       display: flex; | ||||
|       flex-wrap: wrap; | ||||
|       text-align: center; | ||||
|       padding-bottom: 26px; | ||||
|  | ||||
|       .operate-item { | ||||
|         width: 25%; | ||||
|         font-size: 0; | ||||
|         margin-top: 28px; | ||||
|  | ||||
|         &:active { | ||||
|           opacity: 0.7; | ||||
|         } | ||||
|  | ||||
|         h3 { | ||||
|           margin-top: 20px; | ||||
|           color: #666666; | ||||
|           font-size: 26px; | ||||
|           font-weight: normal; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       image { | ||||
|         width: 100px; | ||||
|         height: 100px; | ||||
|         border-radius: 16px; | ||||
|         background: #fff; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   div { | ||||
|     box-sizing: border-box; | ||||
|   } | ||||
|  | ||||
|   .form-item { | ||||
|     margin: 24px 25px 0; | ||||
|     padding: 32px; | ||||
|     background: #FFFFFF; | ||||
|     border-radius: 16px; | ||||
|  | ||||
|     .form-item__bottom { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       margin-top: 28px; | ||||
|       color: #999999; | ||||
|  | ||||
|       i { | ||||
|         width: 12px; | ||||
|         height: 12px; | ||||
|         margin-right: 6px; | ||||
|         border-radius: 50%; | ||||
|         background: #999999; | ||||
|       } | ||||
|  | ||||
|       span { | ||||
|         font-size: 26px; | ||||
|       } | ||||
|  | ||||
|       &.form-item__bottom--active i { | ||||
|         background: #3CB300; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .form-item__top { | ||||
|       display: flex; | ||||
|       justify-content: space-between; | ||||
|       align-items: center; | ||||
|  | ||||
|       .form-item__right { | ||||
|         text-align: center; | ||||
|  | ||||
|         h2 { | ||||
|           line-height: 40px; | ||||
|           margin-bottom: 16px; | ||||
|           font-size: 32px; | ||||
|           font-weight: 600; | ||||
|           color: #1EA0FA; | ||||
|         } | ||||
|  | ||||
|         span { | ||||
|           color: #999999; | ||||
|           font-size: 22px; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .form-item__left { | ||||
|         flex: 1; | ||||
|         max-width: 80%; | ||||
|         position: relative; | ||||
|  | ||||
|         &::after { | ||||
|           position: absolute; | ||||
|           right: -15px; | ||||
|           top: 50%; | ||||
|           width: 2px; | ||||
|           height: 96px; | ||||
|           background: #F5F5F5; | ||||
|           content: ''; | ||||
|           transform: translateY(-50%); | ||||
|         } | ||||
|  | ||||
|         h2 { | ||||
|           line-height: 44px; | ||||
|           margin-bottom: 16px; | ||||
|           color: #333; | ||||
|           font-size: 32px; | ||||
|           font-weight: 700; | ||||
|           overflow: hidden; | ||||
|           text-overflow: ellipsis; | ||||
|           white-space: nowrap; | ||||
|         } | ||||
|  | ||||
|         .form-item__left--info { | ||||
|           display: flex; | ||||
|           align-items: center; | ||||
|           flex-wrap: wrap; | ||||
|           color: #999; | ||||
|           font-size: 20px; | ||||
|  | ||||
|           span { | ||||
|             position: relative; | ||||
|             margin-right: 24px; | ||||
|  | ||||
|             &::after { | ||||
|               position: absolute; | ||||
|               right: -12px; | ||||
|               top: 50%; | ||||
|               width: 2px; | ||||
|               height: 20px; | ||||
|               background: #D1D2D5; | ||||
|               content: ''; | ||||
|               transform: translateY(-50%); | ||||
|             } | ||||
|  | ||||
|             &:last-child { | ||||
|               margin-right: 0; | ||||
|  | ||||
|               &::after { | ||||
|                 display: none; | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .type-0 { | ||||
|     background: #2266FF !important; | ||||
|   } | ||||
|  | ||||
|   .type-1 { | ||||
|     background: rgba(34, 170, 153, 1) !important; | ||||
|   } | ||||
|  | ||||
|   .type-2 { | ||||
|     background: rgba(248, 180, 37, 1) !important; | ||||
|   } | ||||
|  | ||||
|   .type-3 { | ||||
|     background: rgba(102, 119, 187, 1) !important; | ||||
|   } | ||||
|  | ||||
|   .type-4 { | ||||
|     background: rgba(236, 68, 97, 1) !important; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										163
									
								
								src/pages/askForm/config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,163 @@ | ||||
| export const components = [ | ||||
| 	{ | ||||
| 		type: 'radio', | ||||
| 		label: '单选', | ||||
| 		fixedLabel: '单选', | ||||
| 		value: '', | ||||
| 		points: '', | ||||
| 		icon: 'iconradio', | ||||
| 		isShowPoints: false, | ||||
| 		required: true, | ||||
| 		hasAnswer: false, | ||||
| 		answer: '', | ||||
| 		pointType: '0', | ||||
| 		pointDict: [ | ||||
| 			{ | ||||
| 				dictName: '此题有唯一答案和分值', | ||||
| 				dictValue: '0' | ||||
| 			}, | ||||
| 			{ | ||||
| 				dictName: '每个选项都有对应分值', | ||||
| 				dictValue: '1' | ||||
| 			} | ||||
| 		], | ||||
| 		options: [ | ||||
| 			{ | ||||
| 				label: '选项1', | ||||
| 				value: '', | ||||
| 				point: '', | ||||
| 				img: [] | ||||
| 			}, | ||||
| 			{ | ||||
| 				label: '选项2', | ||||
| 				value: '', | ||||
| 				point: '', | ||||
| 				img: [] | ||||
| 			} | ||||
| 		], | ||||
| 		title: '' | ||||
| 	}, | ||||
| 	{ | ||||
| 		type: 'checkbox', | ||||
| 		label: '多选', | ||||
| 		fixedLabel: '多选', | ||||
| 		points: '', | ||||
| 		icon: 'iconcheck_box', | ||||
| 		isShowPoints: false, | ||||
| 		required: true, | ||||
| 		hasAnswer: false, | ||||
| 		answer: [], | ||||
| 		value: [], | ||||
| 		pointType: '0', | ||||
| 		pointDict: [ | ||||
| 			{ | ||||
| 				dictName: '此题有唯一答案和分值', | ||||
| 				dictValue: '0' | ||||
| 			}, | ||||
| 			{ | ||||
| 				dictName: '每个选项都有对应分值', | ||||
| 				dictValue: '1' | ||||
| 			}, | ||||
| 			{ | ||||
| 				dictName: '答对几项得几分,答错不得分', | ||||
| 				dictValue: '2' | ||||
| 			} | ||||
| 		], | ||||
| 		options: [ | ||||
| 			{ | ||||
| 				label: '选项1', | ||||
| 				value: '', | ||||
| 				point: '', | ||||
| 				img: [], | ||||
| 				checked: false | ||||
| 			}, | ||||
| 			{ | ||||
| 				label: '选项2', | ||||
| 				point: '', | ||||
| 				value: '', | ||||
| 				img: [], | ||||
| 				checked: false | ||||
| 			} | ||||
| 		], | ||||
| 		title: '' | ||||
| 	}, | ||||
| 	{ | ||||
| 		type: 'select', | ||||
| 		label: '单下拉框', | ||||
| 		fixedLabel: '单下拉框', | ||||
| 		value: '', | ||||
| 		points: '', | ||||
| 		icon: 'iconSelect', | ||||
| 		isShowPoints: false, | ||||
| 		required: true, | ||||
| 		hasAnswer: false, | ||||
| 		answer: '', | ||||
| 		pointType: '0', | ||||
| 		placeholder: '请选择', | ||||
| 		pointDict: [ | ||||
| 			{ | ||||
| 				dictName: '此题有唯一答案和分值', | ||||
| 				dictValue: '0' | ||||
| 			}, | ||||
| 			{ | ||||
| 				dictName: '每个选项都有对应分值', | ||||
| 				dictValue: '1' | ||||
| 			} | ||||
| 		], | ||||
| 		options: [ | ||||
| 			{ | ||||
| 				label: '选项1', | ||||
| 				value: '', | ||||
| 				point: '', | ||||
| 				img: [] | ||||
| 			}, | ||||
| 			{ | ||||
| 				label: '选项2', | ||||
| 				value: '', | ||||
| 				point: '', | ||||
| 				img: [] | ||||
| 			} | ||||
| 		], | ||||
| 		title: '' | ||||
| 	}, | ||||
| 	{ | ||||
| 		type: 'input', | ||||
| 		label: '单行填空', | ||||
| 		fixedLabel: '单行填空', | ||||
| 		value: '', | ||||
| 		pointType: '0', | ||||
| 		icon: 'icontext_box', | ||||
| 		isShowPoints: false, | ||||
| 		points: '', | ||||
| 		required: true, | ||||
| 		hasAnswer: false, | ||||
| 		placeholder: '请输入...', | ||||
| 		answer: '' | ||||
| 	}, | ||||
| 	{ | ||||
| 		type: 'textarea', | ||||
| 		label: '多行填空', | ||||
| 		fixedLabel: '多行填空', | ||||
| 		pointType: '0', | ||||
| 		icon: 'icontext_area', | ||||
| 		points: '', | ||||
| 		isShowPoints: false, | ||||
| 		required: true, | ||||
| 		hasAnswer: false, | ||||
| 		answer: '', | ||||
| 		placeholder: '请输入...', | ||||
| 		value: '' | ||||
| 	}, | ||||
| 	{ | ||||
| 		type: 'upload', | ||||
| 		label: '上传图片', | ||||
| 		fixedLabel: '上传图片', | ||||
| 		value: '', | ||||
| 		icon: 'iconpic', | ||||
| 		isShowPoints: false, | ||||
| 		points: '', | ||||
| 		required: true, | ||||
| 		hasAnswer: false, | ||||
| 		answer: '' | ||||
| 	} | ||||
| ]; | ||||
							
								
								
									
										535
									
								
								src/pages/askForm/filedConfig.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,535 @@ | ||||
| <template> | ||||
|   <div class="form-config"> | ||||
|     <div class="config-group"> | ||||
|       <div class="config-item"> | ||||
|         <u-input class="form-maintitle" :maxlength="200" v-model="config.label" :placeholder="`请输入${config.fixedLabel}标题 ${config.required ? '(必填)' : ''}`"  placeholder-style="color: #999999; font-weight: 600" /> | ||||
|       </div> | ||||
|       <div class="config-item__select--wrapper" v-if="['radio', 'select', 'checkbox'].includes(config.type)"> | ||||
|         <div class="config-item__select" v-for="(item, index) in config.options" :key="index"> | ||||
|           <image class="config-icon" :src="`${$cdn}askform/del.png`" @click="removeOptions(index)" /> | ||||
|           <div class="config-item__upload" v-if="config.type !== 'select'" @click="upload(index)"> | ||||
|             <u-icon color="#8c9dc3" name="plus" v-if="!item.img.length"></u-icon> | ||||
|             <image v-else :src="item.img[0].url" /> | ||||
|           </div> | ||||
|           <div class="textarea"> | ||||
|            <textarea type="textarea" placeholder-style="color: #CDCDCF" :auto-height="true" v-model="item.label" :maxlength="100" placeholder="请输入选项" /> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="config-item__select config-item__select--add" @click="addOptions"> | ||||
|           <image class="config-icon" :src="`${$cdn}askform/zj.png`" /> | ||||
|           <span>添加选项</span> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="config-group"> | ||||
|       <div class="config-item" v-if="!['radio', 'upload', 'checkbox', 'select'].includes(config.type)"> | ||||
|         <div class="config-item__left"> | ||||
|           <span>说明文字</span> | ||||
|         </div> | ||||
|         <div class="config-item__right"> | ||||
|           <u-input v-model="config.placeholder" placeholder="请输入说明文字" input-align="right" /> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="config-item"> | ||||
|         <div class="config-item__left"> | ||||
|           <span>是否必填</span> | ||||
|         </div> | ||||
|         <div class="config-item__right"> | ||||
|           <u-switch v-model="config.required" active-value="1" inactive-value="0" :size="40" active-color="#1088F9"></u-switch> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="config-item" v-if="!['upload'].includes(config.type)"> | ||||
|         <div class="config-item__left"> | ||||
|           <span>答案与分值</span> | ||||
|         </div> | ||||
|         <div class="config-item__right"> | ||||
|           <u-switch v-model="config.isShowPoints" :size="40" active-color="#1088F9"></u-switch> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="config-item" v-if="['input', 'textarea'].includes(config.type) && config.isShowPoints"> | ||||
|         <div class="config-item__left"> | ||||
|           <span>正确答案</span> | ||||
|         </div> | ||||
|         <div class="config-item__right"> | ||||
|           <u-input v-model="config.answer" placeholder="请输入正确答案" input-align="right" /> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="config-item" v-if="['radio', 'select'].includes(config.type) && config.isShowPoints && config.pointType === '0'"> | ||||
|         <div class="config-item__left"> | ||||
|           <span>正确答案</span> | ||||
|         </div> | ||||
|         <div class="config-item__right config-item__text" @click="isShowAnswer = true"> | ||||
|           <span>{{ config.answer ? config.answer : '请选择正确答案' }}</span> | ||||
|           <u-icon name="arrow-down-fill" color="#c0c4cc" size="24"></u-icon> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="config-item config-item__checkbox" v-if="config.isShowPoints && ['radio', 'select', 'checkbox'].includes(config.type)"> | ||||
|         <div class="config-item__left"> | ||||
|           <span>计分方式</span> | ||||
|         </div> | ||||
|         <div class="config-item__right" @click="isShowType = true"> | ||||
|           <span>{{ pointTypeName ? pointTypeName : '请选择' }}</span> | ||||
|           <u-icon name="arrow-right" color="#E1E2E3" /> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="config-item config-item__answer config-item__checkbox" v-if="['checkbox'].includes(config.type) && config.isShowPoints && config.pointType === '0'"> | ||||
|         <div class="config-item__left"> | ||||
|           <span>正确答案</span> | ||||
|         </div> | ||||
|         <div class="config-item__right"> | ||||
|           <u-checkbox-group wrap @change="onCheckboxChange"> | ||||
|             <u-checkbox v-model="field.checked" :name="field.label"  v-if="field.label" v-for="(field, i) in config.options" :key="i"> | ||||
|               <span>{{ field.label }}</span> | ||||
|             </u-checkbox> | ||||
|           </u-checkbox-group> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="config-item" v-if="config.isShowPoints && config.pointType === '0'"> | ||||
|         <div class="config-item__left"> | ||||
|           <span>本题分值</span> | ||||
|         </div> | ||||
|         <div class="config-item__right"> | ||||
|           <u-input v-model="config.points" type="number" placeholder="请输入本题分值" input-align="right" /> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div v-if="config.isShowPoints && config.pointType === '1'"> | ||||
|         <div class="config-item" v-for="(item, index) in config.options" :key="index"> | ||||
|           <div class="config-item__left"> | ||||
|             <span>{{ item.label }}</span> | ||||
|           </div> | ||||
|           <div class="config-item__right"> | ||||
|             <u-input v-model="item.point" placeholder="请输入分值" input-align="right" /> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="config-item config-item__point" v-if="config.isShowPoints && config.pointType === '2'"> | ||||
|         <u-checkbox-group wrap @change="onCheckboxChange"> | ||||
|           <u-checkbox v-model="field.checked" :name="field.label" v-if="field.label" v-for="(field, i) in config.options" :key="i"> | ||||
|             <span>{{ field.label }}</span> | ||||
|             <u-input v-model="field.point" type="number" placeholder="请输入分值" input-align="right" /> | ||||
|           </u-checkbox> | ||||
|         </u-checkbox-group> | ||||
|       </div> | ||||
|       <div class="config-item" v-if="config.isShowPoints && config.pointType === '2'"> | ||||
|         <div class="config-item__left" style="padding-left: 20px"> | ||||
|           <span>全部答对</span> | ||||
|         </div> | ||||
|         <div class="config-item__right"> | ||||
|           <u-input v-model="config.points" type="number" placeholder="请输入全部答对分值" input-align="right" /> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <u-select :list="config.options" :default-value="defaultAnswer" value-name="value" label-name="label" v-model="isShowAnswer" @confirm="answerChange"></u-select> | ||||
|     <u-select :list="config.pointDict" :default-value="defaultType" value-name="dictValue" label-name="dictName" v-model="isShowType" @confirm="pointTypeChange"></u-select> | ||||
|     <div class="add-form__footer"> | ||||
|       <div @click="back"> | ||||
|         <span>取消</span> | ||||
|       </div> | ||||
|       <div @click="confirm">确定</div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { components } from './config' | ||||
| export default { | ||||
|   data () { | ||||
|     return { | ||||
|       index: '', | ||||
|       isShowType: false, | ||||
|       isShowAnswer: false, | ||||
|       config: { | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   onLoad (query) { | ||||
|     if (query.type) { | ||||
|       this.config = JSON.parse(JSON.stringify(components.filter(v => v.type === query.type)[0])) | ||||
|     } else { | ||||
|       this.config = JSON.parse(query.config) | ||||
|       this.index = query.index | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   computed: { | ||||
|     pointTypeName () { | ||||
|       if (!this.config.pointDict) return '' | ||||
|  | ||||
|       return this.config.pointDict.filter(v => v.dictValue === this.config.pointType)[0].dictName | ||||
|     }, | ||||
|  | ||||
|     defaultType () { | ||||
|       if (!this.config.pointType) return [0] | ||||
|  | ||||
|       return [Number(this.config.pointType)] | ||||
|     }, | ||||
|  | ||||
|     defaultAnswer () { | ||||
|       if (!this.config.answer) return [0] | ||||
|  | ||||
|       let index = 0 | ||||
|       if (this.config.answer) { | ||||
|         this.config.options.forEach((v, i) => { | ||||
|           if (v.label === this.config.answer) { | ||||
|             index = i | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|  | ||||
|       return [index] | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   methods: { | ||||
|     answerChange (e) { | ||||
|       this.config.answer = e[0].label | ||||
|     }, | ||||
|  | ||||
|     pointTypeChange (e) { | ||||
|       console.log(e) | ||||
|       this.config.pointType = e[0].value | ||||
|     }, | ||||
|  | ||||
|     onCheckboxChange (e) { | ||||
|       this.config.answer = e | ||||
|     }, | ||||
|  | ||||
|     upload (index) { | ||||
|       let params = { | ||||
|         count: 1, | ||||
|         sizeType: ['compressed'], | ||||
|         sourceType: ['album', 'camera'], | ||||
|         success: (res) => { | ||||
|           let count = this.fileList?.length + (res.tempFiles?.length || res.tempFile ? 1 : 0) | ||||
|           if (count > 1) { | ||||
|             return this.$u.toast(`不能超过1个`) | ||||
|           } | ||||
|           if (res.tempFiles) { | ||||
|             res.tempFiles.map((item) => { | ||||
|               this.uploadFile(item, index) | ||||
|             }) | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       uni.chooseImage(params) | ||||
|     }, | ||||
|  | ||||
|     uploadFile (img, index) { | ||||
|       uni.showLoading({title: '上传中'}) | ||||
|       let formData = new FormData() | ||||
|       formData.append('file', img) | ||||
|       this.$http.post('/admin/file/add2', formData).then((res) => { | ||||
|         uni.hideLoading() | ||||
|         if (res?.data) { | ||||
|           this.$u.toast('上传成功!') | ||||
|           this.$set(this.config.options[index], 'img', [res.data]) | ||||
|         } | ||||
|       }).catch(res => { | ||||
|         this.$u.toast(res) | ||||
|         uni.hideLoading() | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     removeOptions (index) { | ||||
|       const len = this.config.options.length | ||||
|       const label = this.config.options[index].label | ||||
|       if (len === 2) { | ||||
|         return this.$u.toast('选项不能少于2个') | ||||
|       } | ||||
|  | ||||
|       if (this.config.type === 'checkbox') { | ||||
|         const answerIndex = this.config.answer.indexOf(label) | ||||
|         if (answerIndex > -1) { | ||||
|           this.config.answer.splice(answerIndex, 1) | ||||
|         } | ||||
|       } else { | ||||
|         if (label === this.config.answer) { | ||||
|           this.config.answer = '' | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       this.config.options.splice(index, 1) | ||||
|     }, | ||||
|  | ||||
|     back () { | ||||
|       uni.navigateBack({ | ||||
|         delta: 1 | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     confirm () { | ||||
|       uni.$emit('filedConfig', { | ||||
|         config: this.config, | ||||
|         index: this.index === '' ? '-1' : this.index | ||||
|       }) | ||||
|       uni.navigateBack({ | ||||
|         delta: 1 | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     addOptions () { | ||||
|       const len = this.config.options.length | ||||
|       let label = `选项${len + 1}` | ||||
|  | ||||
|       const index= this.config.options.findIndex(v => label === v.label) | ||||
|       if (index > -1) { | ||||
|         label = `新选项${len + 1}` | ||||
|       } | ||||
|  | ||||
|       this.config.options.push({ | ||||
|         label: label, | ||||
|         value: '', | ||||
|         point: '', | ||||
|         img: '', | ||||
|         checked: false | ||||
|       }) | ||||
|     }, | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .form-config { | ||||
|     box-sizing: border-box; | ||||
|     padding-bottom: 130px; | ||||
|  | ||||
|     .form-maintitle { | ||||
|       ::v-deep .uni-input-input { | ||||
|         font-size: 36px; | ||||
|         font-weight: 600; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .config-item__select--wrapper { | ||||
|       .config-item__select { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|  | ||||
|         ::v-deep .u-input__input { | ||||
|           height: 100%; | ||||
|         } | ||||
|  | ||||
|         .textarea { | ||||
|           flex: 1; | ||||
|           display: flex; | ||||
|           align-items: center; | ||||
|           min-height: 104px; | ||||
|           padding: 16px 0; | ||||
|           font-size: 28px; | ||||
|           border-bottom: 1px solid #dfe8f8; | ||||
|         } | ||||
|  | ||||
|         textarea { | ||||
|           width: 100%; | ||||
|           font-size: 28px; | ||||
|         } | ||||
|  | ||||
|         .config-icon { | ||||
|           width: 36px; | ||||
|           height: 36px; | ||||
|           margin-right: 12px; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .config-item__select--add { | ||||
|         height: 120px; | ||||
|  | ||||
|         .config-icon { | ||||
|           margin-right: 18px; | ||||
|         } | ||||
|  | ||||
|         span { | ||||
|           color: #1D74F4; | ||||
|           font-size: 30px; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .config-group { | ||||
|       margin-bottom: 32px; | ||||
|       padding: 0 32px; | ||||
|       background: #fff; | ||||
|  | ||||
|       .config-item__upload { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         width: 60px; | ||||
|         height: 60px; | ||||
|         margin-right: 20px; | ||||
|         border: 1px solid rgb(208, 212, 220); | ||||
|         background-color: #fbfdff; | ||||
|  | ||||
|         image { | ||||
|           width: 100%; | ||||
|           height: 100%; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .config-item { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: space-between; | ||||
|         min-height: 100px; | ||||
|         padding: 16px 0; | ||||
|         border-bottom: 1px solid #dfe8f8; | ||||
|  | ||||
|         &:last-child { | ||||
|           border: none; | ||||
|         } | ||||
|  | ||||
|         ::v-deep .u-radio__label, ::v-deep .u-checkbox__label { | ||||
|           margin-right: 0; | ||||
|           font-size: 28px; | ||||
|  | ||||
|           span { | ||||
|             max-width: 400rpx; | ||||
|             line-height: 1.2; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         .config-item__left { | ||||
|           max-width: 400px; | ||||
|         } | ||||
|  | ||||
|         .config-item__right { | ||||
|           flex: 1; | ||||
|           text-align: right; | ||||
|           padding-left: 30px; | ||||
|  | ||||
|           &.config-item__text { | ||||
|             display: flex; | ||||
|             align-items: center; | ||||
|             justify-content: flex-end; | ||||
|  | ||||
|             span { | ||||
|               max-width: 400px; | ||||
|               margin-right: 10px; | ||||
|               overflow: hidden; | ||||
|               text-overflow: ellipsis; | ||||
|               white-space: nowrap; | ||||
|               word-break: keep-all; | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           .text { | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         &.config-item__answer { | ||||
|           display: block; | ||||
|           padding: 20px 0; | ||||
|  | ||||
|           .config-item__left { | ||||
|             margin-bottom: 32rpx; | ||||
|             span { | ||||
|               word-break: break-all; | ||||
|               color: #333; | ||||
|               font-size: 30px; | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           .config-item__right { | ||||
|             width: 100%; | ||||
|             padding-left: 0; | ||||
|             text-align: left; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       ::v-deep .u-checkbox { | ||||
|         align-items: baseline; | ||||
|       } | ||||
|  | ||||
|       .config-item__checkbox { | ||||
|         height: auto; | ||||
|         padding: 14px 0; | ||||
|  | ||||
|         ::v-deep .u-checkbox, ::v-deep .u-radio { | ||||
|           // justify-content: flex-end; | ||||
|         } | ||||
|       } | ||||
|       .config-item__point { | ||||
|         height: auto; | ||||
|         padding: 0; | ||||
|  | ||||
|         ::v-deep .u-checkbox { | ||||
|           justify-content: inherit; | ||||
|           min-height: 100px; | ||||
|           padding: 14px 0; | ||||
|           border-bottom: 1px solid #eee; | ||||
|  | ||||
|           &:last-child { | ||||
|             border: none; | ||||
|           } | ||||
|  | ||||
|           .u-checkbox__label { | ||||
|             display: flex; | ||||
|             align-items: center; | ||||
|             justify-content: space-between; | ||||
|             flex: 1; | ||||
|             margin-right: 0; | ||||
|  | ||||
|             .u-input { | ||||
|               flex: 1; | ||||
|               max-width: 400px; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     * { | ||||
|       box-sizing: border-box; | ||||
|     } | ||||
|  | ||||
|     .add-form__footer { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       position: fixed; | ||||
|       left: 0; | ||||
|       bottom: 0; | ||||
|       z-index: 1; | ||||
|       width: 100%; | ||||
|       height: 112px; | ||||
|       text-align: center; | ||||
|  | ||||
|       div { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         flex: 1; | ||||
|         height: 100%; | ||||
|         text-align: center; | ||||
|         background: #fff; | ||||
|  | ||||
|         &:first-child:active { | ||||
|           background: #eee; | ||||
|         } | ||||
|  | ||||
|         &:last-child { | ||||
|           color: #fff; | ||||
|           font-size: 36px; | ||||
|           background: #3192F4; | ||||
|  | ||||
|           &:active { | ||||
|             opacity: 0.8; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         span { | ||||
|           flex: 1; | ||||
|           height: 100%; | ||||
|           line-height: 112px; | ||||
|           color: #333333; | ||||
|           font-size: 32px; | ||||
|  | ||||
|           &:active { | ||||
|             background: #eee; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
							
								
								
									
										405
									
								
								src/pages/askForm/formDetail.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,405 @@ | ||||
| <template> | ||||
|   <section class="formDetail"> | ||||
|     <ai-result v-if="result.tips" v-bind="result"> | ||||
|       <template v-if="isExam" #extra> | ||||
|         <div flex class="scorePane"> | ||||
|           <div>成绩</div> | ||||
|           <div class="fill"><em v-html="score"/> 分</div> | ||||
|         </div> | ||||
|       </template> | ||||
|     </ai-result> | ||||
|     <template v-else-if="form.id"> | ||||
|       <image v-if="form.headPicture" class="headPicture" :src="form.headPicture"/> | ||||
|       <b class="title">{{ form.title || "标题" }}</b> | ||||
|       <div class="tableExplain">{{ form.tableExplain }}</div> | ||||
|       <u-form class="content" label-position="top"> | ||||
|         <u-form-item class="item" v-for="(op,i) in fields" :key="i" :label="(i+1)+'.'+op.fieldName" | ||||
|                      :required="op.fieldInfo.required==1"> | ||||
|           <template v-if="op.fieldType=='input'"> | ||||
|             <input v-model="op.fieldValue" :placeholder="op.fieldInfo.placeholder" :disabled="isResult"/> | ||||
|           </template> | ||||
|           <template v-else-if="op.fieldType=='textarea'"> | ||||
|             <textarea v-model="op.fieldValue" :disabled="isResult" :placeholder="op.fieldInfo.placeholder"/> | ||||
|           </template> | ||||
|           <template v-else-if="op.fieldType=='upload'"> | ||||
|             <ai-uploader @list="v=>op.fieldValue=v.map(e=>e.url)" :def="op.fieldValue" :disabled="isResult" | ||||
|                          preview/> | ||||
|           </template> | ||||
|           <u-row v-else-if="op.fieldType=='radio'"> | ||||
|             <radio-group @change="({detail})=>op.fieldValue=detail.value"> | ||||
|               <div class="option" flex v-for="option in op.fieldInfo.options" :key="option.label"> | ||||
|                 <radio :value="option.label" :disabled="isResult" :checked="op.fieldValue==option.label"/> | ||||
|                 <ai-image v-if="option.img" :src="option.img" preview/> | ||||
|                 <div class="label fill">{{ option.label }}</div> | ||||
|               </div> | ||||
|             </radio-group> | ||||
|           </u-row> | ||||
|           <u-row v-else-if="op.fieldType=='checkbox'"> | ||||
|             <checkbox-group @change="({detail})=>op.fieldValue=detail.value"> | ||||
|               <div class="option" flex v-for="option in op.fieldInfo.options" :key="option.label"> | ||||
|                 <checkbox :value="option.label" :disabled="isResult" | ||||
|                           :checked="option.checked"/> | ||||
|                 <ai-image v-if="option.img" :src="option.img" preview/> | ||||
|                 <div class="label fill">{{ option.label }}</div> | ||||
|               </div> | ||||
|             </checkbox-group> | ||||
|           </u-row> | ||||
|           <template v-else-if="op.fieldType=='select'"> | ||||
|             <ai-select @data="v=>op.fieldValue=v.map(e=>e.value)" :list="op.fieldInfo.options" :disabled="isResult"> | ||||
|               <div class="option" flex> | ||||
|                 <div class="label fill" v-if="op.fieldValue">{{ op.fieldValue.toString() }}</div> | ||||
|                 <i class="fill" v-else>请选择</i> | ||||
|                 <u-icon name="arrow-right" color="#ddd"/> | ||||
|               </div> | ||||
|             </ai-select> | ||||
|           </template> | ||||
|         </u-form-item> | ||||
|       </u-form> | ||||
|       <div class="bottom" v-if="!(isPreview||isResult)"> | ||||
|         <div class="bottomBtn" @tap="handleSubmit">提交</div> | ||||
|       </div> | ||||
|     </template> | ||||
|     <ai-loading v-else tips="调查问卷加载中..."/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|  | ||||
| import UForm from "../../uview/components/u-form/u-form"; | ||||
| import UFormItem from "../../uview/components/u-form-item/u-form-item"; | ||||
| import {mapActions, mapState} from "vuex"; | ||||
| import UInput from "../../uview/components/u-input/u-input"; | ||||
| import AiTextarea from "../../components/AiTextarea"; | ||||
| import AiUploader from "../../components/AiUploader"; | ||||
| import AiSelect from "../../components/AiSelect"; | ||||
| import URadio from "../../uview/components/u-radio/u-radio"; | ||||
| import AiLoading from "../../components/AiLoading"; | ||||
| import AiResult from "../../components/AiResult"; | ||||
| import AiImage from "../../components/AiImage"; | ||||
| import AiBack from "../../components/AiBack"; | ||||
|  | ||||
| export default { | ||||
|   name: "formDetail", | ||||
|   components: { | ||||
|     AiBack, | ||||
|     AiImage, | ||||
|     AiResult, | ||||
|     AiLoading, | ||||
|     URadio, | ||||
|     AiSelect, | ||||
|     AiUploader, | ||||
|     AiTextarea, | ||||
|     UInput, | ||||
|     UFormItem, | ||||
|     UForm | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['openUser', 'token']), | ||||
|     isExam() { | ||||
|       return this.form?.type == 1 | ||||
|     }, | ||||
|     isPreview() { | ||||
|       return !!this.$route.query?.preview | ||||
|     }, | ||||
|     isResult() { | ||||
|       return !!this.$route.query?.result | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       form: {}, | ||||
|       fields: [], | ||||
|       checkUser: false, | ||||
|       result: {}, | ||||
|       score: 0 | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     form: { | ||||
|       deep: true, | ||||
|       handler(v) { | ||||
|         this.fields = v?.fields?.map(e => { | ||||
|           let fieldInfo = JSON.parse(e.fieldInfo) | ||||
|           fieldInfo?.options?.map(op => { | ||||
|             op.img = op?.img?.[0]?.url | ||||
|             op.checked = !!e.fieldValue?.split(",")?.includes(op.label) | ||||
|           }) | ||||
|           if (e.fieldType == 'select') { | ||||
|             fieldInfo.options = fieldInfo.options.map(e => ({...e, value: e.label, label: e.label})) | ||||
|           } | ||||
|           return {...e, fieldInfo} | ||||
|         }) || [] | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     ...mapActions(['getUserInfo']), | ||||
|     getForm() { | ||||
|       let {id} = this.$route.query | ||||
|       this.$http.post("/app/appquestionnairetemplate/queryDetailById", null, { | ||||
|         params: {id} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.form = res.data | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     validateForm() { | ||||
|       return !this.fields.some(e => { | ||||
|         if (!!e?.fieldInfo?.required && !e.fieldValue?.toString()) { | ||||
|           this.$u.toast(e.fieldName + "不能为空!") | ||||
|           return true | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleSubmit() { | ||||
|       if (this.validateForm()) { | ||||
|         this.handleScore() | ||||
|         let {avatar: avatarUrl, openId, name: nickName, type: userType, unionId, corpName} = this.openUser | ||||
|         this.$http.post("/app/appquestionnairetemplate/commit", { | ||||
|           fields: this.fields.map(e => ({ | ||||
|             ...e, | ||||
|             fieldInfo: JSON.stringify(e.fieldInfo), | ||||
|             fieldValue: e.fieldValue?.toString() | ||||
|           })), | ||||
|           avatarUrl, openId, nickName, userType, unionId, corpName, | ||||
|           totalScore: this.score, | ||||
|           questionnaireTemplateId: this.$route.query.id | ||||
|         }).then(res => { | ||||
|           if (res?.code == 0) { | ||||
|             this.result = { | ||||
|               tips: "提交成功!感谢参与", | ||||
|             } | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     }, | ||||
|     handleScore() { | ||||
|       this.score = 0 | ||||
|       this.isExam && this.fields.map(field => { | ||||
|         let item = field?.fieldInfo || {} | ||||
|         let current = 0 | ||||
|         const calcScore = point => (current += (Number(point) || 0)) | ||||
|         if (item?.pointType == 0) {//此题有唯一答案和分值 | ||||
|           field.fieldValue?.toString() == item.answer?.toString() && calcScore(item?.points) | ||||
|         } else if (item?.pointType == 1) {//每个选项都有对应分值 | ||||
|           item?.options?.map(op => { | ||||
|             if (typeof field.fieldValue == "object") { | ||||
|               if (field.fieldValue?.includes(op.label)) { | ||||
|                 calcScore(op.point) | ||||
|               } | ||||
|             } else { | ||||
|               op.label == field.fieldValue && calcScore(op.point) | ||||
|             } | ||||
|           }) | ||||
|         } else if (item?.pointType == 2) {//答对几项得几分,答错不得分 | ||||
|           item?.options?.some(op => { | ||||
|             if (typeof field.fieldValue == "object") { | ||||
|               if (field.fieldValue?.includes(op.label)) { | ||||
|                 if (item.answer?.includes(op.label)) calcScore(op.point) | ||||
|                 else return current = 0 | ||||
|               } | ||||
|             } else { | ||||
|               op.label == field.fieldValue && calcScore(op.point) | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|         this.score += current | ||||
|         //打印每题打分 | ||||
|         if (!!field.fieldValue) { | ||||
|           const typeResult = (reply, answer) => { | ||||
|             console.log("题目:%s,回答:%s,得分:%s,总分:%s \n 答案:%s", field.fieldName, | ||||
|                 reply, current, this.score, answer) | ||||
|           } | ||||
|           typeResult(field.fieldValue?.toString(), item.answer?.toString()) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     checkForm() { | ||||
|       if (this.isPreview) { | ||||
|         this.checkUser = true | ||||
|         return Promise.resolve() | ||||
|       } | ||||
|       let {query: {id}, hash} = this.$route, | ||||
|           {openId} = this.openUser | ||||
|       if (hash != "#form") { | ||||
|         this.result = { | ||||
|           tips: "非法的调查问卷链接", | ||||
|           status: "error" | ||||
|         } | ||||
|       } else if (openId) { | ||||
|         return new Promise(resolve => { | ||||
|           this.$http.post("/app/appquestionnairetemplate/commitCheck", null, { | ||||
|             params: {id, openId} | ||||
|           }).then(res => { | ||||
|             if (res?.code == 0) { | ||||
|               this.checkUser = true | ||||
|               if (this.isResult && res?.data) { | ||||
|                 this.form = res?.data | ||||
|               } else resolve() | ||||
|             } else this.result = { | ||||
|               tips: "调查问卷加载失败", | ||||
|               status: "error", | ||||
|               btn: "重新加载", | ||||
|               btnTap() { | ||||
|                 location.reload() | ||||
|               } | ||||
|             } | ||||
|           }).catch(err => { | ||||
|             this.result = { | ||||
|               tips: err || "调查问卷加载失败", | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       } else { | ||||
|         this.getUserInfo().then(() => { | ||||
|           if (!!this.openUser?.openId) { | ||||
|             this.checkForm()?.then(() => this.checkUser && this.getForm()) | ||||
|           } else { | ||||
|             this.result = { | ||||
|               tips: "您的信息获取失败", | ||||
|               status: "error", | ||||
|               btn: "重新加载", | ||||
|               btnTap() { | ||||
|                 location.reload() | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     }, | ||||
|   }, | ||||
|   created() { | ||||
|     this.checkForm()?.then(() => this.checkUser && this.getForm()) | ||||
|   }, | ||||
|   mounted() { | ||||
|     document.title = this.form.title || "调查问卷" | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .formDetail { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   background: #fff; | ||||
|  | ||||
|   .headPicture { | ||||
|     width: 100%; | ||||
|     height: 320px; | ||||
|  | ||||
|     .img { | ||||
|       width: 100%; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .title { | ||||
|     width: 100%; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
|     flex-wrap: wrap; | ||||
|     padding: 32px; | ||||
|     box-sizing: border-box; | ||||
|     font-size: 34px; | ||||
|     font-weight: bold; | ||||
|     color: #333; | ||||
|     line-height: 48px; | ||||
|   } | ||||
|  | ||||
|   .bottom { | ||||
|     position: fixed; | ||||
|     bottom: 0; | ||||
|     width: 100%; | ||||
|     padding: 32px; | ||||
|     box-sizing: border-box; | ||||
|     background: #fff; | ||||
|     z-index: 99; | ||||
|  | ||||
|     .bottomBtn { | ||||
|       width: 100%; | ||||
|       line-height: 96px; | ||||
|       background: #287DE1; | ||||
|       color: #fff; | ||||
|       text-align: center; | ||||
|       height: 96px; | ||||
|       font-size: 32px; | ||||
|       font-weight: bold; | ||||
|       border-radius: 8px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .tableExplain { | ||||
|     font-size: 28px; | ||||
|     font-weight: 400; | ||||
|     color: #666; | ||||
|     padding: 32px 24px; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .u-form-item { | ||||
|     .u-form-item--left { | ||||
|       font-size: 30px; | ||||
|       font-weight: bold; | ||||
|       color: #333; | ||||
|     } | ||||
|  | ||||
|     .u-form-item--right__content__slot > * { | ||||
|       width: 100%; | ||||
|     } | ||||
|  | ||||
|     .display { | ||||
|       justify-content: space-between; | ||||
|       min-height: 58px; | ||||
|     } | ||||
|  | ||||
|     .uni-radio-input, .uni-checkbox-input { | ||||
|       height: 32px; | ||||
|       width: 32px; | ||||
|     } | ||||
|  | ||||
|     .label { | ||||
|       flex-shrink: 0; | ||||
|  | ||||
|       * + & { | ||||
|         margin-left: 16px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .option { | ||||
|       width: 100%; | ||||
|       margin-right: 16px; | ||||
|       margin-bottom: 16px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .content { | ||||
|     padding: 64px 32px 200px; | ||||
|     background: #fff; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .scorePane { | ||||
|     width: calc(100% - 40px); | ||||
|     padding: 0 32px; | ||||
|     height: 124px; | ||||
|     background: #E9F2FF; | ||||
|     border-radius: 16px; | ||||
|     font-size: 30px; | ||||
|     font-weight: 500; | ||||
|     color: #333333; | ||||
|     margin-top: 48px; | ||||
|     box-sizing: border-box; | ||||
|  | ||||
|     .fill { | ||||
|       text-align: center; | ||||
|     } | ||||
|  | ||||
|     em { | ||||
|       font-size: 48px; | ||||
|       font-weight: bold; | ||||
|       color: #2C72FE; | ||||
|       font-style: normal; | ||||
|       margin-right: 8px; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										164
									
								
								src/pages/askForm/formList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,164 @@ | ||||
| <template> | ||||
|   <section class="formList"> | ||||
|     <ai-top-fixed> | ||||
|       <u-search placeholder="请输入标题" :show-action="false" search-icon-color="#ccc" | ||||
|                 v-model="search.title" @search="page.current=1,getList()"/> | ||||
|  | ||||
|     </ai-top-fixed> | ||||
|     <div class="mainPane"> | ||||
|       <div class="formBox column" flex v-for="op in list" :key="op.id"> | ||||
|         <div flex> | ||||
|           <div class="fill column" flex> | ||||
|             <b class="title">{{ op.title }}</b> | ||||
|             <div class="info wrap" flex> | ||||
|               <span v-html="op.createUserName"/> | ||||
|               <span v-html="op.createUnitName"/> | ||||
|               <span v-html="op.createTime"/> | ||||
|               <span v-html="$dict.getLabel('questionnaireType',op.type)"/> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div class="split"/> | ||||
|           <div flex class="column submitCount"> | ||||
|             <b>{{ op.dataCount }}</b> | ||||
|             <div>答卷数量</div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div flex class="bottom"> | ||||
|           <div class="dot" :style="{background:$dict.getColor('questionnaireStatus',op.status)}"/> | ||||
|           <div>{{ $dict.getLabel("questionnaireStatus", op.status) }}</div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import AiTopFixed from "../../components/AiTopFixed"; | ||||
|  | ||||
| export default { | ||||
|   name: "formList", | ||||
|   components: {AiTopFixed}, | ||||
|   data() { | ||||
|     return { | ||||
|       page: {current: 1, size: 10, total: 0}, | ||||
|       search: {title: ""}, | ||||
|       list: [] | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getList() { | ||||
|       this.$http.post("/app/appquestionnairetemplate/list", null, { | ||||
|         params: {...this.page, ...this.search} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           if (this.page.current > 1) { | ||||
|             this.list = [...this.list, ...res.data.records] | ||||
|           } else this.list = res.data.records | ||||
|           this.page.total = res.data.total | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     reachBottom() { | ||||
|       if (this.page.total > this.list.length) { | ||||
|         this.page.current++ | ||||
|         this.getList() | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.$dict.load("questionnaireStatus", 'questionnaireType') | ||||
|     this.getList() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .formList { | ||||
|   min-height: 100%; | ||||
|   background: #F3F6F9; | ||||
|  | ||||
|   ::v-deep .mainPane { | ||||
|     padding: 24px 24px 126px; | ||||
|  | ||||
|     .formBox { | ||||
|       width: 100%; | ||||
|       min-height: 220px; | ||||
|       background: #FFFFFF; | ||||
|       border-radius: 16px; | ||||
|       padding: 24px; | ||||
|       box-sizing: border-box; | ||||
|       text-align: center; | ||||
|       color: #999; | ||||
|       font-size: 20px; | ||||
|       margin-bottom: 24px; | ||||
|  | ||||
|       & > div { | ||||
|         width: 100%; | ||||
|       } | ||||
|  | ||||
|       b { | ||||
|         width: 100%; | ||||
|         white-space: nowrap; | ||||
|         overflow: hidden; | ||||
|         text-overflow: ellipsis; | ||||
|         font-size: 32px; | ||||
|         color: #333; | ||||
|         margin-bottom: 16px; | ||||
|       } | ||||
|  | ||||
|       .title { | ||||
|         text-align: start; | ||||
|       } | ||||
|  | ||||
|       .info { | ||||
|         width: 100%; | ||||
|  | ||||
|         span { | ||||
|           white-space: nowrap; | ||||
|           padding: 0 16px; | ||||
|           box-sizing: border-box; | ||||
|           border-left: 1px solid #D1D2D5; | ||||
|           margin-bottom: 4px; | ||||
|  | ||||
|           &:first-of-type { | ||||
|             border-left: none; | ||||
|             padding-left: 0; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .bottom { | ||||
|         margin-top: 30px; | ||||
|         font-size: 26px; | ||||
|  | ||||
|         .dot { | ||||
|           width: 11px; | ||||
|           height: 11px; | ||||
|           background: #3CB300; | ||||
|           border-radius: 50%; | ||||
|           margin-right: 8px; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .submitCount { | ||||
|         width: 118px; | ||||
|  | ||||
|         b { | ||||
|           color: #1EA0FA; | ||||
|         } | ||||
|  | ||||
|         & > div { | ||||
|           font-size: 22px; | ||||
|           white-space: nowrap; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .split { | ||||
|         width: 2px; | ||||
|         background: #f5f5f5; | ||||
|         margin: 0 24px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										314
									
								
								src/pages/askForm/formSetting.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,314 @@ | ||||
| <template> | ||||
|   <div class="form-setting"> | ||||
|     <h2>表单设置</h2> | ||||
|     <div class="form-setting__list"> | ||||
|       <div class="setting-item"> | ||||
|         <div class="setting-item__left"> | ||||
|           <span>截止时间</span> | ||||
|           <image :src="`${$cdn}askform/bz.png`" @click="tips = '表单截止后,用户打开表单会提示此表单已结束' , isShowModal = true" /> | ||||
|         </div> | ||||
|         <div class="setting-item__right"> | ||||
|           <u-radio-group v-model="periodValidityType" active-color="#1088F9"> | ||||
|             <u-radio name="0">永久有效</u-radio> | ||||
|             <u-radio name="1">自定义时间</u-radio> | ||||
|           </u-radio-group> | ||||
|           <u-icon name="arrow-right" color="#E1E2E3" /> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="setting-item" v-if="periodValidityType === '1'" @click="isShowTime = true"> | ||||
|         <div class="setting-item__left"> | ||||
|           <span>截至时间</span> | ||||
|         </div> | ||||
|         <div class="setting-item__right"> | ||||
|           <span>{{ periodValidityEndTime }}</span> | ||||
|           <u-icon name="arrow-right" color="#E1E2E3" /> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="setting-item"> | ||||
|         <div class="setting-item__left"> | ||||
|           <span>匹配客户方式</span> | ||||
|           <image :src="`${$cdn}askform/bz.png`" @click="tips = '将参与活动的微信客户和企业微信客户匹配' , isShowModal = true" /> | ||||
|         </div> | ||||
|         <div class="setting-item__right"> | ||||
|           <span>客户微信ID匹配</span> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="setting-item"> | ||||
|         <div class="setting-item__left"> | ||||
|           <span>提交次数</span> | ||||
|           <image :src="`${$cdn}askform/bz.png`" @click="tips = '此功能发布后不可修改' , isShowModal = true" /> | ||||
|         </div> | ||||
|         <div class="setting-item__right"> | ||||
|           <u-radio-group v-model="commitType" active-color="#1088F9"> | ||||
|             <u-radio name="0">不限次数</u-radio> | ||||
|             <u-radio name="1">限提交一次</u-radio> | ||||
|           </u-radio-group> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="setting-item"> | ||||
|         <div class="setting-item__left"> | ||||
|           <span>行为通知</span> | ||||
|           <image :src="`${$cdn}askform/bz.png`" @click="tips = '当客户点击或者发布表单时,发送表单的员工将会受到消息提醒' , isShowModal = true" /> | ||||
|         </div> | ||||
|         <div class="setting-item__right"> | ||||
|           <u-switch v-model="actionNotice" active-value="1" inactive-value="0" :size="40" active-color="#1088F9"></u-switch> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="setting-item"> | ||||
|         <div class="setting-item__left"> | ||||
|           <span>动态通知</span> | ||||
|           <image :src="`${$cdn}askform/bz.png`" @click="tips = '当客户点击或者发布表单时,会将客户的打开行为记录在客户动态里' , isShowModal = true" /> | ||||
|         </div> | ||||
|         <div class="setting-item__right"> | ||||
|           <u-switch v-model="dynamicNotice" active-value="1" inactive-value="0" :size="40" active-color="#1088F9"></u-switch> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="add-form__footer"> | ||||
|       <div @click="back"> | ||||
|         <span>取消</span> | ||||
|       </div> | ||||
|       <div @click="confirm">{{ type === 'edit' ? '发布' : '确定' }}</div> | ||||
|     </div> | ||||
|     <u-modal v-model="isShowModal" :content="tips"></u-modal> | ||||
|     <u-picker mode="time" v-model="isShowTime" :show-time-tag="true" @confirm="onTimeChange" :params="params"></u-picker> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   data () { | ||||
|     return { | ||||
|       params: { | ||||
|         year: true, | ||||
|         month: true, | ||||
|         day: true, | ||||
|         hour: true, | ||||
|         minute: true, | ||||
|         second: true | ||||
|       }, | ||||
|       tips: '', | ||||
|       isShowModal: false, | ||||
|       actionNotice: true, | ||||
|       dynamicNotice: true, | ||||
|       commitType: '1', | ||||
|       wechatId: '0', | ||||
|       periodValidityEndTime: '', | ||||
|       isShowTime: false, | ||||
|       periodValidityType: '0', | ||||
|       type: '', | ||||
|       id: '' | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   onLoad (query) { | ||||
|     if (query.id) { | ||||
|       this.id = query.id | ||||
|       this.getInfo(query.id) | ||||
|       this.type = query.type | ||||
|     } else if (query.formConfig && query.formConfig !== '{}') { | ||||
|       const res = JSON.parse(query.formConfig) | ||||
|       this.periodValidityType = res.periodValidityType | ||||
|       this.commitType = res.commitType | ||||
|       this.actionNotice = res.actionNotice === '1' | ||||
|       this.dynamicNotice = res.dynamicNotice === '1' | ||||
|  | ||||
|       if (res.periodValidityType === '1') { | ||||
|         this.periodValidityEndTime = res.periodValidityEndTime | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   methods: { | ||||
|     onTimeChange (e) { | ||||
|       this.periodValidityEndTime = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}` | ||||
|     }, | ||||
|  | ||||
|     back () { | ||||
|       uni.navigateBack({ | ||||
|         delta: 1 | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     getInfo (id) { | ||||
|       this.$http.post(`/app/appquestionnairetemplate/queryDetailById?id=${id}`).then(res => { | ||||
|         if (res.code == 0) { | ||||
|           this.periodValidityType = res.data.periodValidityType | ||||
|           this.commitType = res.data.commitType | ||||
|           this.actionNotice = res.data.actionNotice === '1' | ||||
|           this.dynamicNotice = res.data.dynamicNotice === '1' | ||||
|  | ||||
|           if (res.data.periodValidityType === '1') { | ||||
|             this.periodValidityEndTime = res.data.periodValidityEndTime | ||||
|           } | ||||
|         } | ||||
|       }).catch(msg => { | ||||
|         this.$u.toast(msg) | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     publish () { | ||||
|       this.$http.post(`/app/appquestionnairetemplate/release`, null, { | ||||
|         params: { | ||||
|           commitType: this.commitType, | ||||
|           periodValidityType: this.periodValidityType, | ||||
|           actionNotice: this.actionNotice ? '1' : '0', | ||||
|           dynamicNotice: this.dynamicNotice ? '1' : '0', | ||||
|           shareStatus: '0', | ||||
|           wechatId: '0', | ||||
|           id: this.id, | ||||
|           periodValidityEndTime: this.periodValidityType === '1' ? this.periodValidityEndTime : '' | ||||
|         } | ||||
|       }).then(res => { | ||||
|         if (res.code == 0) { | ||||
|           uni.$emit('reload') | ||||
|           this.$u.toast('发布成功') | ||||
|  | ||||
|           this.back() | ||||
|         } | ||||
|       }).catch(e => { | ||||
|         this.$u.toast(e) | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     confirm () { | ||||
|       if (this.type === 'edit') { | ||||
|         this.publish() | ||||
|  | ||||
|         return false | ||||
|       } | ||||
|  | ||||
|       uni.$emit('setting', { | ||||
|         periodValidityType: this.periodValidityType, | ||||
|         commitType: this.commitType, | ||||
|         actionNotice: this.actionNotice ? '1' : '0', | ||||
|         dynamicNotice: this.dynamicNotice ? '1' : '0', | ||||
|         periodValidityEndTime: this.periodValidityEndTime ? this.periodValidityEndTime : '' | ||||
|       }) | ||||
|  | ||||
|       this.back() | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .form-setting { | ||||
|     padding: 0 20px; | ||||
|  | ||||
|     .add-form__footer { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       position: fixed; | ||||
|       left: 0; | ||||
|       bottom: 0; | ||||
|       z-index: 1; | ||||
|       width: 100%; | ||||
|       height: 112px; | ||||
|       text-align: center; | ||||
|  | ||||
|       div { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         flex: 1; | ||||
|         height: 100%; | ||||
|         text-align: center; | ||||
|         background: #fff; | ||||
|  | ||||
|         &:first-child:active { | ||||
|           background: #eee; | ||||
|         } | ||||
|  | ||||
|         &:last-child { | ||||
|           color: #fff; | ||||
|           font-size: 36px; | ||||
|           background: #3192F4; | ||||
|  | ||||
|           &:active { | ||||
|             opacity: 0.8; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         span { | ||||
|           flex: 1; | ||||
|           height: 100%; | ||||
|           line-height: 112px; | ||||
|           color: #333333; | ||||
|           font-size: 32px; | ||||
|  | ||||
|           &:active { | ||||
|             background: #eee; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     * { | ||||
|       box-sizing: border-box; | ||||
|     } | ||||
|  | ||||
|     & > h2 { | ||||
|       height: 80px; | ||||
|       padding-top: 24px; | ||||
|       font-size: 28px; | ||||
|       color: #999999; | ||||
|     } | ||||
|  | ||||
|     .form-setting__list { | ||||
|       padding: 0 20px; | ||||
|       background: #fff; | ||||
|       border-radius: 8px; | ||||
|  | ||||
|       .setting-item { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: space-between; | ||||
|         height: 104px; | ||||
|         border-bottom: 1px solid #D8DDE6; | ||||
|  | ||||
|         .setting-item__right { | ||||
|           color: #999; | ||||
|           font-size: 28px; | ||||
|  | ||||
|           span { | ||||
|             margin-right: 6px; | ||||
|           } | ||||
|  | ||||
|           ::v-deep .u-radio__label { | ||||
|             color: #999; | ||||
|             font-size: 28px; | ||||
|           } | ||||
|  | ||||
|           ::v-deep .u-radio { | ||||
|             &:last-child { | ||||
|               .u-radio__label { | ||||
|                 margin-right: 6px; | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         &:last-child { | ||||
|           border: none; | ||||
|         } | ||||
|  | ||||
|         & > div { | ||||
|           display: flex; | ||||
|           align-items: center; | ||||
|         } | ||||
|  | ||||
|         .setting-item__left { | ||||
|           color: #666666; | ||||
|           font-size: 30px; | ||||
|  | ||||
|           image { | ||||
|             width: 30px; | ||||
|             height: 30px; | ||||
|             margin-left: 16px; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
							
								
								
									
										69
									
								
								src/pages/askForm/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,69 @@ | ||||
| <template> | ||||
|   <div class="form"> | ||||
|     <div class="form-content"> | ||||
|       <add-list ref="addList" v-if="currIndex === 1"></add-list> | ||||
|       <list ref="list" v-if="currIndex === 0"></list> | ||||
|     </div> | ||||
|     <ai-tabbar :active.sync="currIndex" :list="tabBar"/> | ||||
|     <div class="form-footer"></div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import addList from './components/addList.vue' | ||||
| import list from './components/list.vue' | ||||
| import AiTabbar from '../../components/AiTabbar' | ||||
|  | ||||
| export default { | ||||
|   name: 'formIndex', | ||||
|   label: '问卷表单', | ||||
|  | ||||
|   data () { | ||||
|     return { | ||||
|       currIndex: 0 | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   components: { | ||||
|     addList, | ||||
|     AiTabbar, | ||||
|     list | ||||
|   }, | ||||
|  | ||||
|   computed: { | ||||
|     tabBar () { | ||||
|       const link = icon => `${this.$cdn}askform/${icon}.png` | ||||
|       return [ | ||||
|         {text: "表单列表", iconPath: "bdlb1", selectedIconPath: "bdlb2" }, | ||||
|         {text: "新建项目", iconPath: "xjxm1", selectedIconPath: "xjxm2" } | ||||
|       ].map(e => ({ | ||||
|         ...e, | ||||
|         iconPath: link(e.iconPath), | ||||
|         selectedIconPath: link(e.selectedIconPath) | ||||
|       })) | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   onLoad () { | ||||
|     uni.$on('reload', () => { | ||||
|       if (this.currIndex === 0) { | ||||
|         this.$refs.list.reload() | ||||
|       } else { | ||||
|         this.$refs.addList.getList() | ||||
|       } | ||||
|     }) | ||||
|   }, | ||||
|  | ||||
|   onReachBottom() { | ||||
|     if (this.currIndex === 0) { | ||||
|       this.$refs.list.getList() | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .form { | ||||
|     padding-bottom: 98px; | ||||
|   } | ||||
| </style> | ||||
							
								
								
									
										457
									
								
								src/pages/askForm/previewForm.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,457 @@ | ||||
| <template> | ||||
|   <div class="add-form"> | ||||
|     <div class="header-pic"> | ||||
|       <image v-if="form.headPicture" :src="form.headPicture" /> | ||||
|     </div> | ||||
|     <div class="form-info"> | ||||
|       <div class="form-info__wrapper"> | ||||
|         <textarea class="title" :auto-height="true" disabled placeholder="请输入标题 (必填)" v-model="form.title"></textarea> | ||||
|         <u-input class="content" disabled :clearable="false" type="textarea" v-model="form.tableExplain" placeholder="请输入表单描述 (选填)" :height="80" :auto-height="true" :maxlength="255"></u-input> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="components-list"> | ||||
|       <div class="components-item" v-for="(item, index) in targetList" :key="index"> | ||||
|         <div class="components-item__title"> | ||||
|           <div class="components-item__title--left"> | ||||
|             <em :style="{opacity: item.required ? 1 : 0}">*</em> | ||||
|             <i>{{ index + 1 }}.</i> | ||||
|             <h2>{{ item.label }}</h2> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="components-item__filed"> | ||||
|           <template v-if="(item.type === 'radio')"> | ||||
|             <u-radio-group v-model="item.value" wrap> | ||||
|               <u-radio :name="field.label" v-for="(field, i) in item.options" :key="i"> | ||||
|                 <image :src="field.img[0].url" v-if="field.img.length"/> | ||||
|                 <span>{{ field.label }}</span> | ||||
|               </u-radio> | ||||
|             </u-radio-group> | ||||
|           </template> | ||||
|           <template v-if="(item.type === 'checkbox')"> | ||||
|             <u-checkbox-group wrap> | ||||
|               <u-checkbox :name="field.label" v-model="field.checked1" v-for="(field, i) in item.options" :key="i"> | ||||
|                 <image :src="field.img[0].url" v-if="field.img.length"/> | ||||
|                 <span>{{ field.label }}</span> | ||||
|               </u-checkbox> | ||||
|             </u-checkbox-group> | ||||
|           </template> | ||||
|           <template v-if="(item.type === 'select')"> | ||||
|             <div class="components-item__select"> | ||||
|               <span>{{ item.placeholder }}</span> | ||||
|               <u-icon name="arrow-down" color="#DEDFDF" /> | ||||
|             </div> | ||||
|           </template> | ||||
|           <template v-if="(item.type === 'upload')"> | ||||
|             <div class="components-item__select components-item__textarea components-item__upload"> | ||||
|               <image :src="`${$cdn}askform/upload.png`" /> | ||||
|               <span>选择图片(2M以内)</span> | ||||
|             </div> | ||||
|           </template> | ||||
|           <template v-if="(item.type === 'input')"> | ||||
|             <div class="components-item__select"> | ||||
|               <span>{{ item.placeholder }}</span> | ||||
|             </div> | ||||
|           </template> | ||||
|           <template v-if="(item.type === 'textarea')"> | ||||
|             <div class="components-item__select components-item__textarea"> | ||||
|               <span>{{ item.placeholder }}</span> | ||||
|             </div> | ||||
|           </template> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <AiBack></AiBack> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import AiBack from "@/components/AiBack"; | ||||
|   export default { | ||||
|     data () { | ||||
|       return { | ||||
|         form: { | ||||
|           tableExplain: '详细描述', | ||||
|           title: '问卷调查', | ||||
|           isShowheadPicture: true, | ||||
|           isShowTableExplain: true, | ||||
|           isShowBtn: true, | ||||
|           headPicture: '', | ||||
|           commitType: '1', | ||||
|           periodValidityType: '0', | ||||
|           actionNotice: '1', | ||||
|           dynamicNotice: '1', | ||||
|           periodValidityEndTime: '', | ||||
|           shareStatus: '0', | ||||
|           count: 0, | ||||
|           wechatId: '0', | ||||
|           type: 0, | ||||
|           buttonExplain: '提交', | ||||
|           tips: true | ||||
|         }, | ||||
|         templateType: 0, | ||||
|         targetList: [], | ||||
|         isShow: false, | ||||
|         type: 0, | ||||
|         id: '', | ||||
|         touchStart: 0 | ||||
|       } | ||||
|     }, | ||||
|     onLoad (query) { | ||||
|       this.form = JSON.parse(query.form) | ||||
|       this.targetList = JSON.parse(query.targetList) | ||||
|     }, | ||||
|  | ||||
|     components: { | ||||
|       AiBack | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       getInfo (id) { | ||||
|         uni.showLoading() | ||||
|         this.$http.post(`/app/appquestionnairetemplate/queryDetailById?id=${id}`).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.form = { | ||||
|               ...res.data, | ||||
|               headPicture: res.data.headPicture | ||||
|             } | ||||
|  | ||||
|             this.type = res.data.type | ||||
|  | ||||
|             this.targetList = res.data.fields.map(item => { | ||||
|               return JSON.parse(item.fieldInfo) | ||||
|             }) | ||||
|  | ||||
|             this.pageShow = true | ||||
|           } else { | ||||
|             this.$u.toast(res.msg) | ||||
|           } | ||||
|  | ||||
|           uni.hideLoading() | ||||
|         }).catch(() => { | ||||
|           uni.hideLoading() | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .add-form { | ||||
|     min-height: 100vh; | ||||
|     padding-bottom: 60px; | ||||
|     box-sizing: border-box; | ||||
|     background: #F3F6F9; | ||||
|  | ||||
|     * { | ||||
|       box-sizing: border-box; | ||||
|     } | ||||
|  | ||||
|     ::v-deep .u-drawer-bottom { | ||||
|       background-color: transparent; | ||||
|     } | ||||
|  | ||||
|     .components-list { | ||||
|       padding: 0 20px; | ||||
|  | ||||
|       .components-item { | ||||
|         margin-top: 24px; | ||||
|         padding: 32px; | ||||
|         box-shadow: 0 4px 8px 4px rgba(233, 233, 233, 0.39); | ||||
|         border-radius: 8px; | ||||
|         overflow: hidden; | ||||
|         border: 1px solid #EEEFF0; | ||||
|         background: #fff; | ||||
|  | ||||
|         ::v-deep .u-radio, ::v-deep .u-checkbox { | ||||
|           position: relative; | ||||
|           margin-bottom: 20px; | ||||
|  | ||||
|           &:last-child { | ||||
|             margin-bottom: 0; | ||||
|           } | ||||
|  | ||||
|           .u-checkbox__icon-wrap, .u-radio__icon-wrap { | ||||
|             position: absolute; | ||||
|             top: 4px; | ||||
|           } | ||||
|  | ||||
|           .u-radio__label, .u-checkbox__label { | ||||
|             display: flex; | ||||
|             align-items: center; | ||||
|             margin-right: 0; | ||||
|             margin-left: 40px; | ||||
|             line-height: 1.5; | ||||
|             text-align: justify; | ||||
|  | ||||
|             image { | ||||
|               width: 100px; | ||||
|               height: 100px; | ||||
|               margin: 0 10px; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         span { | ||||
|           flex: 1; | ||||
|           color: #666; | ||||
|           font-size: 26px; | ||||
|         } | ||||
|  | ||||
|         .components-item__select { | ||||
|           display: flex; | ||||
|           align-items: center; | ||||
|           justify-content: space-between; | ||||
|           width: 100%; | ||||
|           height: 80px; | ||||
|           margin-bottom: 8px; | ||||
|           padding: 0 26px; | ||||
|           border: 1px solid #DEDFDF; | ||||
|  | ||||
|           &.components-item__textarea { | ||||
|             align-items: flex-start; | ||||
|             height: 160px; | ||||
|             padding-top: 20px; | ||||
|  | ||||
|             image { | ||||
|               width: 46px; | ||||
|               height: 34px; | ||||
|               margin-right: 16px; | ||||
|             } | ||||
|  | ||||
|             span { | ||||
|               color: #666; | ||||
|               font-size: 26px; | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           &.components-item__upload { | ||||
|             justify-content: center; | ||||
|             align-items: center; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         .components-item__title { | ||||
|           display: flex; | ||||
|           justify-content: space-between; | ||||
|           margin-bottom: 32px; | ||||
|  | ||||
|           em { | ||||
|             margin-right: 4px; | ||||
|             font-style: normal; | ||||
|             color: rgb(226, 33, 32);; | ||||
|           } | ||||
|  | ||||
|           image { | ||||
|             width: 32px; | ||||
|             height: 32px; | ||||
|             box-sizing: content-box; | ||||
|             padding: 20px 0 20px 20px; | ||||
|           } | ||||
|  | ||||
|           div { | ||||
|             display: flex; | ||||
|             align-items: baseline; | ||||
|             color: #333333; | ||||
|             font-size: 32px; | ||||
|  | ||||
|             i { | ||||
|               font-style: normal; | ||||
|             } | ||||
|  | ||||
|             h2 { | ||||
|               font-weight: 600; | ||||
|               font-size: 32px; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .add-popup { | ||||
|       height: 440px; | ||||
|       border-radius: 20px 20px 0 0; | ||||
|       background: #fff; | ||||
|  | ||||
|       .add-popup__title { | ||||
|         display: flex; | ||||
|         position: relative; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         height: 96px; | ||||
|         border-bottom: 1px solid #E4E5E6; | ||||
|  | ||||
|         h2 { | ||||
|           color: #333333; | ||||
|           font-size: 32px; | ||||
|           font-weight: 600; | ||||
|         } | ||||
|  | ||||
|         image { | ||||
|           position: absolute; | ||||
|           right: 32px; | ||||
|           top: 50%; | ||||
|           width: 30px; | ||||
|           height: 20px; | ||||
|           transform: translateY(-50%); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .add-popup__list { | ||||
|         display: flex; | ||||
|         flex-wrap: wrap; | ||||
|         padding: 0 34px; | ||||
|  | ||||
|         span { | ||||
|           width: calc((100% - 64px) / 3); | ||||
|           height: 78px; | ||||
|           line-height: 78px; | ||||
|           margin-top: 32px; | ||||
|           margin-right: 32px; | ||||
|           text-align: center; | ||||
|           color: #333333; | ||||
|           font-size: 28px; | ||||
|           border-radius: 8px; | ||||
|           border: 1px solid #E4E5E6; | ||||
|  | ||||
|           &:nth-of-type(3n) { | ||||
|             margin-right: 0; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .add-form__footer { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       position: fixed; | ||||
|       left: 0; | ||||
|       bottom: 0; | ||||
|       z-index: 1; | ||||
|       width: 100%; | ||||
|       height: 112px; | ||||
|       text-align: center; | ||||
|  | ||||
|       div { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         flex: 1; | ||||
|         height: 100%; | ||||
|         text-align: center; | ||||
|         background: #fff; | ||||
|  | ||||
|         &:last-child { | ||||
|           color: #fff; | ||||
|           font-size: 36px; | ||||
|           background: #3192F4; | ||||
|  | ||||
|           &:active { | ||||
|             opacity: 0.8; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         span { | ||||
|           flex: 1; | ||||
|           height: 100%; | ||||
|           line-height: 112px; | ||||
|           color: #333333; | ||||
|           font-size: 32px; | ||||
|  | ||||
|           &:active { | ||||
|             background: #eee; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .add-form__btn { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       justify-content: center; | ||||
|       width: 214px; | ||||
|       height: 66px; | ||||
|       line-height: 66px; | ||||
|       margin: 64px auto 0; | ||||
|       background: #FFFFFF; | ||||
|       border-radius: 34px; | ||||
|  | ||||
|       &:active { | ||||
|         opacity: 0.8; | ||||
|       } | ||||
|  | ||||
|       image { | ||||
|         width: 28px; | ||||
|         height: 28px; | ||||
|       } | ||||
|  | ||||
|       span { | ||||
|         margin-left: 16px; | ||||
|         color: #4392E6; | ||||
|         font-size: 28px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     * { | ||||
|       box-sizing: border-box; | ||||
|     } | ||||
|  | ||||
|     .form-info { | ||||
|       margin-top: 26px; | ||||
|       padding: 0 20px; | ||||
|  | ||||
|       & > h2 { | ||||
|         height: 76px; | ||||
|         line-height: 76px; | ||||
|         color: #999999; | ||||
|         font-weight: normal; | ||||
|         font-size: 28px; | ||||
|       } | ||||
|  | ||||
|       .form-info__wrapper { | ||||
|         padding: 0 18px; | ||||
|         background: #fff; | ||||
|  | ||||
|         .title { | ||||
|           width: 100%; | ||||
|           padding: 22px 0; | ||||
|           font-size: 36px; | ||||
|           border-bottom: 1px solid #F1F2F3; | ||||
|         } | ||||
|  | ||||
|         .content { | ||||
|           padding: 30px 0!important; | ||||
|           font-size: 28px; | ||||
|  | ||||
|           ::v-deep textarea { | ||||
|             color: #333; | ||||
|             font-size: 28px!important; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .header-pic { | ||||
|       position: relative; | ||||
|       font-size: 0; | ||||
|  | ||||
|       span { | ||||
|         position: absolute; | ||||
|         bottom: 16px; | ||||
|         right: 16px; | ||||
|         z-index: 1; | ||||
|         width: 148px; | ||||
|         height: 56px; | ||||
|         line-height: 56px; | ||||
|         text-align: center; | ||||
|         color: #fff; | ||||
|         font-size: 26px; | ||||
|         background: rgba(0, 0, 0, 0.16); | ||||
|         border-radius: 28px; | ||||
|       } | ||||
|  | ||||
|       image { | ||||
|         width: 100%; | ||||
|         height: 320px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
							
								
								
									
										501
									
								
								src/pages/bigHorn/addPlay.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,501 @@ | ||||
| <template> | ||||
|   <div class="addPlay"> | ||||
|     <div class="content"> | ||||
|       <div class="item"> | ||||
|         <div class="label">播发内容</div> | ||||
|         <div class="value" @click="linkTo('/pages/resourcesManage/resourcesManage?isChoose=1')"> | ||||
|           <span :class="formData.mediaName == '请选择' ? 'color-999' : ''">{{formData.mediaName}}</span> | ||||
|           <img src="./img/right-icon.png" alt=""> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="item"> | ||||
|         <div class="label">播放设备</div> | ||||
|         <div class="value" @click="selectClick('showEquipment', equipmentList)"> | ||||
|           <span :class="formData.serialName == '请选择' ? 'color-999' : ''">{{formData.serialName}}</span> | ||||
|           <img src="./img/right-icon.png" alt=""> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="item"> | ||||
|         <div class="label">播发级别</div> | ||||
|         <div class="value" @click="selectClick('showMessageLevel', messageLevelList)"> | ||||
|           <span :class="formData.messageLevelName == '请选择' ? 'color-999' : ''">{{formData.messageLevelName}}</span> | ||||
|           <img src="./img/right-icon.png" alt=""> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="radio-content"> | ||||
|       <div class="title">播放方式</div> | ||||
|       <div class="item mar-r50" :class="formData.taskType == 0 ? 'active' : ''"  @click="formData.taskType = 0">立即播放<img src="./img/bigHorn-xz.png" alt=""></div> | ||||
|       <div class="item" :class="formData.taskType == 1 ? 'active' : ''" @click="formData.taskType = 1"><img src="./img/bigHorn-xz.png" alt="">定时播放</div> | ||||
|     </div> | ||||
|     <div class="content" v-if="formData.taskType != 0"> | ||||
|       <div class="item"> | ||||
|         <div class="label">定时策略</div> | ||||
|         <div class="value" @click="selectClick('showCyclingType', cyclingTypeList)"> | ||||
|           <span :class="formData.cyclingTypeName == '请选择' ? 'color-999' : ''">{{formData.cyclingTypeName}}</span> | ||||
|           <img src="./img/right-icon.png" alt=""> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="radio-content" v-if="formData.taskType != 0 && formData.cyclingType == 2"> | ||||
|       <div class="title">播放天数</div> | ||||
|       <div class="mini-item" :class="item.isCheck ? 'mini-active' : ''" v-for="(item, index) in dayList" :key="index" @click="checkClick(index)">{{item.label}}</div> | ||||
|     </div> | ||||
|     <div class="content" v-if="formData.taskType != 0 && formData.cyclingType == 3"> | ||||
|       <div class="item"> | ||||
|         <div class="label">播放天数</div> | ||||
|         <div class="value"> | ||||
|           <u-input type="text" placeholder="请输入" height="18" input-align="right" v-model="formData.broadcastDay" maxlength="4" /> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="content" v-if="formData.taskType != 0"> | ||||
|       <div class="item"> | ||||
|         <div class="label">开始日期</div> | ||||
|         <div class="value" @click="timeClick(true, 'showDate')"> | ||||
|           <span :class="formData.startDate ? 'color-999' : ''">{{formData.startDate || '请选择'}}</span> | ||||
|           <img src="./img/right-icon.png" alt=""> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="item"> | ||||
|         <div class="label">开始时间</div> | ||||
|         <div class="value" @click="timeClick(false, 'showSatrt')"> | ||||
|           <span :class="formData.startTime ? 'color-999' : ''">{{formData.startTime || '请选择'}}</span> | ||||
|           <img src="./img/right-icon.png" alt=""> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="item"> | ||||
|         <div class="label">结束时间</div> | ||||
|         <div class="value" @click="timeClick(false, 'showEnd')"> | ||||
|           <span :class="formData.endTime ? 'color-999' : ''">{{formData.endTime || '请选择'}}</span> | ||||
|           <img src="./img/right-icon.png" alt=""> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="btn" @click="addConfirm">确认</div> | ||||
|     <u-select v-model="showSelect" :list="selectList" @confirm="confirm" label-name="dictName" value-name="dictValue"></u-select> | ||||
|     <u-picker v-model="showDateTime" mode="time" :params="params" @confirm="confirm"></u-picker> | ||||
|     <AiBack></AiBack> | ||||
|   </div> | ||||
| </template> | ||||
| <script>  | ||||
|   import AiBack from "@/components/AiBack"; | ||||
| export default { | ||||
|   name: "addPlay", | ||||
|   data() { | ||||
|     return { | ||||
|       showSelect: false, | ||||
|       selectList: [], | ||||
|       showMedia: false, | ||||
|       mediaList: [], | ||||
|       showEquipment: false, | ||||
|       equipmentList: [], | ||||
|       showMessageLevel: false, | ||||
|       messageLevelList: [], | ||||
|       showCyclingType: false, | ||||
|       cyclingTypeList: [], | ||||
|       formData: { | ||||
|         mediaId: '', | ||||
|         mediaName: '请选择', | ||||
|         serialNo: '', | ||||
|         serialName: '请选择', | ||||
|         messageLevel: '', | ||||
|         messageLevelName: '请选择', | ||||
|         taskType: '0', | ||||
|         cyclingTypeName: '请选择', | ||||
|         cyclingType: '', | ||||
|         startDate: '', | ||||
|         startTime: '', | ||||
|         endTime: '', | ||||
|         broadcastDay: '', | ||||
|         cyclingDate: '' | ||||
|       }, | ||||
|       dayList: [ | ||||
|         { | ||||
|           isCheck: false, | ||||
|           value: 1, | ||||
|           label: '每周一' | ||||
|         }, | ||||
|         { | ||||
|           isCheck: false, | ||||
|           value: 2, | ||||
|           label: '每周二' | ||||
|         }, | ||||
|         { | ||||
|           isCheck: false, | ||||
|           value: 3, | ||||
|           label: '每周三' | ||||
|         }, | ||||
|         { | ||||
|           isCheck: false, | ||||
|           value: 4, | ||||
|           label: '每周四' | ||||
|         }, | ||||
|         { | ||||
|           isCheck: false, | ||||
|           value: 5, | ||||
|           label: '每周五' | ||||
|         }, | ||||
|         { | ||||
|           isCheck: false, | ||||
|           value: 6, | ||||
|           label: '每周六' | ||||
|         }, | ||||
|         { | ||||
|           isCheck: false, | ||||
|           value: 7, | ||||
|           label: '每周日' | ||||
|         } | ||||
|       ], | ||||
|       showDateTime: false, | ||||
|       showDate: false, | ||||
|       showSatrt: false, | ||||
|       showEnd: false, | ||||
|       params: { | ||||
|         year: true, | ||||
|         month: true, | ||||
|         day: true, | ||||
|         hour: false, | ||||
|         minute: false, | ||||
|         second: false | ||||
|       }, | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   components: { | ||||
|     AiBack | ||||
|   }, | ||||
|  | ||||
|   onLoad () { | ||||
|     uni.$on('choose', e => { | ||||
|       console.log(e) | ||||
|       this.formData.mediaId = e.mediaId | ||||
|       this.formData.mediaName = e.mediaName | ||||
|     }) | ||||
|   }, | ||||
|  | ||||
|   methods: { | ||||
|     addConfirm() { | ||||
|       var cyclingDateList = [] | ||||
|       this.dayList.map((item) => { | ||||
|         if(item.isCheck) { | ||||
|           cyclingDateList.push(item.value) | ||||
|         } | ||||
|       }) | ||||
|  | ||||
|       if(!this.formData.mediaId) { | ||||
|         return this.$u.toast('请选择播发内容') | ||||
|       } | ||||
|       if(!this.formData.serialNo) { | ||||
|         return this.$u.toast('请选择播放设备') | ||||
|       } | ||||
|       if(!this.formData.messageLevel) { | ||||
|         return this.$u.toast('播发级别') | ||||
|       } | ||||
|  | ||||
|       //播放方式(定时播放) | ||||
|       if(this.formData.taskType != 0 && this.formData.startDate == '请选择') { | ||||
|         return this.$u.toast('请选择开始日期') | ||||
|       } | ||||
|       if(this.formData.taskType != 0 && this.formData.startTime == '请选择') { | ||||
|         return this.$u.toast('请选择开始时间') | ||||
|       } | ||||
|       if(this.formData.taskType != 0 && this.formData.endTime == '请选择') { | ||||
|         return this.$u.toast('请选择结束时间') | ||||
|       } | ||||
|       //播放方式(定时播放)定时策略(时长) | ||||
|       if(this.formData.taskType != 0 && this.formData.cyclingType == 3 && !this.formData.broadcastDay) { | ||||
|         return this.$u.toast('请输入播放天数') | ||||
|       } | ||||
|       //播放方式(定时播放)定时策略(自定义) | ||||
|       if(this.formData.taskType != 0 && this.formData.cyclingType == 2 && !cyclingDateList.length) { | ||||
|         return this.$u.toast('请选择播放天数') | ||||
|       } | ||||
|        | ||||
|       if(this.formData.taskType != 0 && this.formData.cyclingType == 2) { | ||||
|         this.formData.cyclingDate = cyclingDateList.join(',') | ||||
|       } | ||||
|       this.formData.coverageType = '4' | ||||
|       this.$http.post(`/app/appzyvideobroadcast/play`, {...this.formData,}).then((res) => { | ||||
|         if (res.code == 0) { | ||||
|           this.$u.toast('提交成功') | ||||
|           setTimeout(() => { | ||||
|             uni.navigateBack() | ||||
|           }, 1000) | ||||
|         } | ||||
|       })  | ||||
|     }, | ||||
|  | ||||
|     linkTo (path) { | ||||
|       uni.navigateTo({ | ||||
|         url: path | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     confirm(e) { | ||||
|       if(this.showMedia) { | ||||
|         this.formData.mediaId = e[0].value | ||||
|         this.formData.mediaName = e[0].label | ||||
|       } | ||||
|       if(this.showEquipment) { | ||||
|         this.formData.serialNo = e[0].value | ||||
|         this.formData.serialName = e[0].label | ||||
|       } | ||||
|       if(this.showMessageLevel) { | ||||
|         this.formData.messageLevel = e[0].value | ||||
|         this.formData.messageLevelName = e[0].label | ||||
|       } | ||||
|       if(this.showCyclingType) { | ||||
|         this.formData.cyclingType = e[0].value | ||||
|         this.formData.cyclingTypeName = e[0].label | ||||
|       } | ||||
|       if(this.showDate) { | ||||
|         this.formData.startDate = e.year + '-' + e.month + '-' + e.day | ||||
|       } | ||||
|       if(this.showSatrt) { | ||||
|         var startTime = e.hour + ':' + e.minute + ':' + e.second | ||||
|         var myDate = new Date(); | ||||
|         var time = myDate.getHours() + ':' +  myDate.getMinutes() + ':' + myDate.getSeconds() | ||||
|         if (this.timeToSec(startTime) - this.timeToSec(time) > 0) { | ||||
|           this.formData.startTime = startTime | ||||
|         }else { | ||||
|           this.$u.toast('开始时间要大于当前时间') | ||||
|         } | ||||
|       } | ||||
|       if(this.showEnd) { | ||||
|         var endTime = e.hour + ':' + e.minute + ':' + e.second | ||||
|         console.log(this.timeToSec(endTime), this.timeToSec(this.formData.startTime)) | ||||
|         if (this.timeToSec(endTime) - this.timeToSec(this.formData.startTime) > 0) { | ||||
|           this.formData.endTime = endTime | ||||
|         } else { | ||||
|           this.$u.toast('结束时间要大于开始时间') | ||||
|         } | ||||
|       } | ||||
|       this.init() | ||||
|     }, | ||||
|     init() { | ||||
|       this.showMedia = false | ||||
|       this.showEquipment = false | ||||
|       this.showMessageLevel = false | ||||
|       this.showCyclingType = false | ||||
|       this.showDate = false | ||||
|       this.showSatrt = false | ||||
|       this.showEnd = false | ||||
|     }, | ||||
|     selectClick(showType, list) { | ||||
|       this.showSelect = true | ||||
|       this[showType] = true | ||||
|       this.selectList = list | ||||
|     }, | ||||
|     getMediaList() { | ||||
|       this.$http.post(`/app/appdlbresource/list?current=1&size=10000`).then((res) => { | ||||
|         if (res.code == 0) { | ||||
|           this.mediaList = [] | ||||
|           if(res.data && res.data.records.length) { | ||||
|             res.data.records.map((item) => { | ||||
|               let info = { | ||||
|                 dictName: item.name, | ||||
|                 dictValue: item.id | ||||
|               } | ||||
|               this.mediaList.push(info) | ||||
|             }) | ||||
|           } | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     getEquipmentList() { | ||||
|       this.$http.post(`/app/appdlbquipment/getDlbDeviceList?current=1&size=10000&devStatus=5&keyword=`).then((res) => { | ||||
|         if (res.code == 0) { | ||||
|           this.equipmentList = [] | ||||
|           if(res.data && res.data.records.length) { | ||||
|             res.data.records.map((item) => { | ||||
|               let info = { | ||||
|                 dictName: item.deviceName, | ||||
|                 dictValue: item.serialNo | ||||
|               } | ||||
|               this.equipmentList.push(info) | ||||
|             }) | ||||
|           } | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     checkClick(index) { | ||||
|       this.dayList[index].isCheck = !this.dayList[index].isCheck | ||||
|     }, | ||||
|     timeClick(showYear, showType) { | ||||
|       this[showType] = true | ||||
|       this.showDateTime = true | ||||
|       if(showYear) { | ||||
|         this.params = { | ||||
|           year: true, | ||||
|           month: true, | ||||
|           day: true, | ||||
|           hour: false, | ||||
|           minute: false, | ||||
|           second: false | ||||
|         } | ||||
|       }else { | ||||
|         this.params = { | ||||
|           year: false, | ||||
|           month: false, | ||||
|           day: false, | ||||
|           hour: true, | ||||
|           minute: true, | ||||
|           second: true | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     timeToSec(time) { | ||||
|       var s = ""; | ||||
|       var hour = time.split(":")[0]; | ||||
|       var min = time.split(":")[1]; | ||||
|       var second =  time.split(":")[2]; | ||||
|       s = Number(hour * 3600) + Number(min * 60) + Number(second) | ||||
|       return s; | ||||
|     },  | ||||
|   }, | ||||
|   created() { | ||||
|     this.$dict.load('dlbMessageUrgency', 'dlbBroadTaskType', 'dlbDyclingType').then(() => { | ||||
|       this.getMediaList() | ||||
|       this.getEquipmentList() | ||||
|       this.messageLevelList = this.$dict.getDict('dlbMessageUrgency') | ||||
|       this.cyclingTypeList = this.$dict.getDict('dlbDyclingType') | ||||
|     }) | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .addPlay { | ||||
|   padding-bottom: 128px; | ||||
|   .content{ | ||||
|     padding-left: 32px; | ||||
|     background-color: #fff; | ||||
|     .item{ | ||||
|       width: 100%; | ||||
|       padding: 34px 0; | ||||
|       font-size: 32px; | ||||
|       font-family: PingFangSC-Regular, PingFang SC; | ||||
|       font-weight: 400; | ||||
|       line-height: 44px; | ||||
|       border-bottom: 1px solid #ddd; | ||||
|       display: flex; | ||||
|       color: #333; | ||||
|       justify-content: space-between; | ||||
|       .label{ | ||||
|         width: 198px; | ||||
|         font-size: 32px; | ||||
|       } | ||||
|       .value{ | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: flex-end; | ||||
|         font-size: 28px; | ||||
|         flex: 1; | ||||
|         padding-right: 32px; | ||||
|         max-width: calc(100% - 198px); | ||||
|         box-sizing: border-box; | ||||
|         text-align: right; | ||||
|  | ||||
|         span { | ||||
|           overflow: hidden; | ||||
|           text-overflow: ellipsis; | ||||
|           white-space: nowrap; | ||||
|         } | ||||
|         img{ | ||||
|           width: 32px; | ||||
|           height: 32px; | ||||
|           vertical-align: middle; | ||||
|           margin-left: 6px; | ||||
|         } | ||||
|       } | ||||
|       .color-999{ | ||||
|         color: #999; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   .radio-content{ | ||||
|     padding: 34px 32px 38px; | ||||
|     border-bottom: 1px solid #ddd; | ||||
|     background-color: #fff; | ||||
|     .title{ | ||||
|       font-size: 32px; | ||||
|       font-family: PingFangSC-Regular, PingFang SC; | ||||
|       font-weight: 400; | ||||
|       color: #333; | ||||
|       line-height: 44px; | ||||
|       margin-bottom: 54px; | ||||
|       span{ | ||||
|         font-size: 24px; | ||||
|         font-weight: 400; | ||||
|       } | ||||
|     } | ||||
|     .item{ | ||||
|       display: inline-block; | ||||
|       width: 320px; | ||||
|       height: 112px; | ||||
|       line-height: 112px; | ||||
|       text-align: center; | ||||
|       background: #F5F5F5; | ||||
|       border-radius: 4px; | ||||
|       font-size: 30px; | ||||
|       font-family: PingFangSC-Medium, PingFang SC; | ||||
|       font-weight: 500; | ||||
|       color: #333; | ||||
|       img{ | ||||
|         display: none; | ||||
|       } | ||||
|     } | ||||
|     .active{ | ||||
|       background: #E7F1FE; | ||||
|       color: #1174FE; | ||||
|       position: relative; | ||||
|       img{ | ||||
|         display: inline-block; | ||||
|         position: absolute; | ||||
|         bottom: 0; | ||||
|         right: 0; | ||||
|         width: 46px; | ||||
|         height: 46px; | ||||
|       } | ||||
|     } | ||||
|     .mar-r50 { | ||||
|       margin-right: 50px; | ||||
|     } | ||||
|     .mini-item{ | ||||
|       display: inline-block; | ||||
|       width: 128px; | ||||
|       height: 72px; | ||||
|       line-height: 72px; | ||||
|       text-align: center; | ||||
|       background: #F9F9F9; | ||||
|       border-radius: 16px; | ||||
|       color: #333; | ||||
|       font-size: 28px; | ||||
|       margin-right: 58px; | ||||
|       margin-bottom: 32px; | ||||
|     } | ||||
|     .mini-item:nth-of-type(5) { | ||||
|       margin-right: 0; | ||||
|     } | ||||
|     .mini-active{ | ||||
|       background: #F2F8FE; | ||||
|       border: 1px solid #89B2EE; | ||||
|       box-sizing: border-box; | ||||
|     } | ||||
|   } | ||||
|   .btn{ | ||||
|     position: fixed; | ||||
|     bottom: 0; | ||||
|     left: 0; | ||||
|     width: 100%; | ||||
|     height: 112px; | ||||
|     line-height: 112px; | ||||
|     text-align: center; | ||||
|     background: #3975C6; | ||||
|     font-size: 32px; | ||||
|     font-family: PingFangSC-Medium, PingFang SC; | ||||
|     font-weight: 500; | ||||
|     color: #FFFFFF; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										167
									
								
								src/pages/bigHorn/bigHorn.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,167 @@ | ||||
| <template> | ||||
|   <div class="bigHorn"> | ||||
|     <div class="header"> | ||||
|       <img src="./img/bigHorn-bg.png" alt=""> | ||||
|       <div class="content"> | ||||
|         <div class="item" @click="linkTo('./onlineList')"> | ||||
|           <img src="./img/bigHorn-icon1@2x.png" alt=""> | ||||
|           <div>在线设备</div> | ||||
|           <!-- <h2>1</h2> --> | ||||
|         </div> | ||||
|         <div class="item" @click="linkTo('./playList')"> | ||||
|           <img src="./img/bigHorn-icon2@2x.png" alt=""> | ||||
|           <div>播放记录</div> | ||||
|         </div> | ||||
|         <div class="item" @click="linkTo('./onlinePlayList')"> | ||||
|           <img src="./img/bigHorn-icon3@2x.png" alt=""> | ||||
|           <div>在播设备</div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="banner"> | ||||
|       <div class="item" :class="item.bgClass" v-for="(item, index) in bannerList" :key="index" @click="linkTo(item.path)"> | ||||
|         <h2>{{item.title}}</h2> | ||||
|         <div>{{item.text}}</div> | ||||
|         <img :src="item.imgUrl" alt=""> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| <script>  | ||||
| export default { | ||||
|   name: "bigHorn", | ||||
|   data() { | ||||
|     return { | ||||
|       bannerList: [ | ||||
|         { | ||||
|           title: '素材播放', | ||||
|           text: '支持音频立即播发和定时播发', | ||||
|           imgUrl: require('./img/bigHorn-icon11@2x.png'), | ||||
|           path: '/pages/bigHorn/addPlay', | ||||
|           bgClass: 'bg-67A3F4' | ||||
|         }, | ||||
|         { | ||||
|           title: '实时喊话', | ||||
|           text: '实时在线喊话,远程广播通知', | ||||
|           imgUrl: require('./img/bigHorn-icon22@2x.png'), | ||||
|           bgClass: 'bg-4ED5BB' | ||||
|         }, | ||||
|         { | ||||
|           title: '音频录制', | ||||
|           text: '音频文件的录制', | ||||
|           path: '/pages/resourcesManage/addPlay?type=1', | ||||
|           imgUrl: require('./img/bigHorn-icon33@2x.png'), | ||||
|           bgClass: 'bg-E5B565' | ||||
|         }, | ||||
|         { | ||||
|           title: '媒资管理', | ||||
|           path: '/pages/resourcesManage/resourcesManage', | ||||
|           text: '支持音频文件和录音内容添加', | ||||
|           imgUrl: require('./img/bigHorn-icon44@2x.png'), | ||||
|           bgClass: 'bg-F19661' | ||||
|         } | ||||
|       ] | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     linkTo(url) { | ||||
|       uni.navigateTo({url}) | ||||
|     } | ||||
|   }, | ||||
|   mounted() { | ||||
|  | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .bigHorn { | ||||
|   .header{ | ||||
|     position: relative; | ||||
|     img{ | ||||
|       width: 100%; | ||||
|       height: 306px; | ||||
|     } | ||||
|     .content{ | ||||
|       width: 686px; | ||||
|       padding: 40px 0; | ||||
|       background: #FFFFFF; | ||||
|       box-shadow: 0px 4px 8px 4px rgba(0, 0, 0, 0.06); | ||||
|       border-radius: 12px; | ||||
|       position: absolute; | ||||
|       top: 210px; | ||||
|       left: 50%; | ||||
|       transform: translateX(-50%); | ||||
|       display: flex; | ||||
|       .item{ | ||||
|         text-align: center; | ||||
|         flex: 1; | ||||
|         img{ | ||||
|           width: 64px; | ||||
|           height: 64px; | ||||
|           margin-bottom: 18px; | ||||
|         } | ||||
|         div{ | ||||
|           font-size: 30px; | ||||
|           font-family: PingFang-SC-Medium, PingFang-SC; | ||||
|           font-weight: 500; | ||||
|           color: #333; | ||||
|           line-height: 42px; | ||||
|           margin-bottom: 10px; | ||||
|         } | ||||
|         h2{ | ||||
|           font-size: 40px; | ||||
|           font-family: DINAlternate-Bold, DINAlternate; | ||||
|           font-weight: bold; | ||||
|           color: #333; | ||||
|           line-height: 48px; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   .banner{ | ||||
|     margin-top: 150px; | ||||
|     .item{ | ||||
|       width: 686px; | ||||
|       height: 190px; | ||||
|       border-radius: 12px; | ||||
|       margin: 0 auto 24px auto; | ||||
|       padding: 40px 46px 0 80px; | ||||
|       box-sizing: border-box; | ||||
|       color: #FFF; | ||||
|       position: relative; | ||||
|       h2{ | ||||
|         font-size: 42px; | ||||
|         font-family: PingFangSC-Medium, PingFang SC; | ||||
|         font-weight: 500; | ||||
|         line-height: 60px; | ||||
|         margin-bottom: 16px; | ||||
|       } | ||||
|       div{ | ||||
|         font-size: 26px; | ||||
|         font-family: PingFangSC-Regular, PingFang SC; | ||||
|         font-weight: 400; | ||||
|         color: #FFF; | ||||
|         line-height: 32px; | ||||
|       } | ||||
|       img{ | ||||
|         width: 160px; | ||||
|         position: absolute; | ||||
|         top: 10px; | ||||
|         right: 30px; | ||||
|       } | ||||
|     } | ||||
|     .bg-67A3F4{ | ||||
|       background: #67A3F4; | ||||
|     } | ||||
|     .bg-4ED5BB{ | ||||
|       background: #4ED5BB; | ||||
|     } | ||||
|     .bg-E5B565{ | ||||
|       background: #E5B565; | ||||
|     } | ||||
|     .bg-F19661{ | ||||
|       background: #F19661; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										
											BIN
										
									
								
								src/pages/bigHorn/img/bigHorn-bg.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 64 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/pages/bigHorn/img/bigHorn-icon11@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 6.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/pages/bigHorn/img/bigHorn-icon1@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/pages/bigHorn/img/bigHorn-icon22@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/pages/bigHorn/img/bigHorn-icon2@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/pages/bigHorn/img/bigHorn-icon33@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 6.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/pages/bigHorn/img/bigHorn-icon3@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/pages/bigHorn/img/bigHorn-icon44@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/pages/bigHorn/img/bigHorn-lb@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/pages/bigHorn/img/bigHorn-xz.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/pages/bigHorn/img/cir.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 795 B | 
							
								
								
									
										
											BIN
										
									
								
								src/pages/bigHorn/img/lb@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/pages/bigHorn/img/right-icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 269 B | 
							
								
								
									
										
											BIN
										
									
								
								src/pages/bigHorn/img/search-icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 766 B | 
							
								
								
									
										
											BIN
										
									
								
								src/pages/bigHorn/img/select-blue.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 KiB | 
							
								
								
									
										93
									
								
								src/pages/bigHorn/onlineList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,93 @@ | ||||
| <template> | ||||
|   <div class="onlineList"> | ||||
|     <div class="record"> | ||||
|       <div class="item"  v-for="(item, index) in list" :key="index"> | ||||
|         <img src="./img/bigHorn-lb@2x.png" alt=""> | ||||
|         <div class="info"> | ||||
|           <p>{{item.deviceName}}</p> | ||||
|           <span>{{item.areaName}}</span> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <ai-empty v-if="!list.length"></ai-empty> | ||||
|     <AiBack></AiBack> | ||||
|   </div> | ||||
| </template> | ||||
| <script>  | ||||
| import AiEmpty from '@/components/AiEmpty/AiEmpty' | ||||
| import AiBack from "@/components/AiBack"; | ||||
| export default { | ||||
|   name: "onlineList", | ||||
|   data() { | ||||
|     return { | ||||
|       page: {current: 1, size: 10, total: 0}, | ||||
|       list: [] | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   components: { | ||||
|     AiEmpty, | ||||
|     AiBack | ||||
|   }, | ||||
|   methods: { | ||||
|     getList() { | ||||
|       this.$http.post("/app/appdlbquipment/getDlbDeviceList", null, { | ||||
|         params: {...this.page, devStatus: 5} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           if (this.page.current > 1) { | ||||
|             this.list = [...this.list, ...res.data.records] | ||||
|           } else this.list = res.data.records | ||||
|           this.page.total = res.data.total | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     reachBottom() { | ||||
|       if (this.page.total > this.list.length) { | ||||
|         this.page.current++ | ||||
|         this.getList() | ||||
|       } | ||||
|     }, | ||||
|   }, | ||||
|   created() { | ||||
|     this.getList() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .onlineList { | ||||
|   .record{ | ||||
|     padding-left: 32px; | ||||
|     background-color: #fff; | ||||
|     .item{ | ||||
|       width: 100%; | ||||
|       border-bottom: 1px solid #ddd; | ||||
|       display: flex; | ||||
|       padding: 12px 40px 16px 0; | ||||
|       img{ | ||||
|         width: 48px; | ||||
|         height: 48px; | ||||
|         margin: 12px 16px 0 0; | ||||
|       } | ||||
|       .info{ | ||||
|         width: calc(100% - 100px); | ||||
|         p{ | ||||
|           font-size: 34px; | ||||
|           font-family: PingFang-SC-Medium, PingFang-SC; | ||||
|           font-weight: 500; | ||||
|           color: #333; | ||||
|           line-height: 48px; | ||||
|           margin-bottom: 12px; | ||||
|         } | ||||
|         span{ | ||||
|           font-size: 22px; | ||||
|           font-family: PingFang-SC-Medium, PingFang-SC; | ||||
|           font-weight: 500; | ||||
|           color: #999; | ||||
|           line-height: 32px; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										95
									
								
								src/pages/bigHorn/onlinePlayList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,95 @@ | ||||
| <template> | ||||
|   <div class="onlinePlayList"> | ||||
|     <div class="record"> | ||||
|       <div class="item" v-for="(item, index) in list" :key="index"> | ||||
|         <img src="./img/bigHorn-lb@2x.png" alt=""> | ||||
|         <div class="info"> | ||||
|           <p>{{item.deviceName}}</p> | ||||
|           <span>{{item.createTime}}</span><br /> | ||||
|           <span>{{item.name}}</span> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <ai-empty v-if="!list.length"></ai-empty> | ||||
|     <AiBack></AiBack> | ||||
|   </div> | ||||
| </template> | ||||
| <script>  | ||||
| import AiEmpty from '@/components/AiEmpty/AiEmpty' | ||||
| import AiBack from "@/components/AiBack"; | ||||
| export default { | ||||
|   name: "onlinePlayList", | ||||
|   data() { | ||||
|     return { | ||||
|       page: {current: 1, size: 10, total: 0}, | ||||
|       list: [] | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   components: { | ||||
|     AiEmpty, | ||||
|     AiBack | ||||
|   }, | ||||
|  | ||||
|   methods: { | ||||
|     getList() { | ||||
|       this.$http.post("/app/appdlbquipment/getDlbDeviceList", null, { | ||||
|         params: {...this.page, devStatus: 1} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           if (this.page.current > 1) { | ||||
|             this.list = [...this.list, ...res.data.records] | ||||
|           } else this.list = res.data.records | ||||
|           this.page.total = res.data.total | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     reachBottom() { | ||||
|       if (this.page.total > this.list.length) { | ||||
|         this.page.current++ | ||||
|         this.getList() | ||||
|       } | ||||
|     }, | ||||
|   }, | ||||
|   created() { | ||||
|     this.getList() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .onlinePlayList { | ||||
|   .record{ | ||||
|     padding-left: 32px; | ||||
|     background-color: #fff; | ||||
|     .item{ | ||||
|       width: 100%; | ||||
|       border-bottom: 1px solid #ddd; | ||||
|       display: flex; | ||||
|       padding: 12px 40px 16px 0; | ||||
|       img{ | ||||
|         width: 48px; | ||||
|         height: 48px; | ||||
|         margin: 12px 16px 0 0; | ||||
|       } | ||||
|       .info{ | ||||
|         width: calc(100% - 100px); | ||||
|         p{ | ||||
|           font-size: 34px; | ||||
|           font-family: PingFang-SC-Medium, PingFang-SC; | ||||
|           font-weight: 500; | ||||
|           color: #333; | ||||
|           line-height: 48px; | ||||
|           margin-bottom: 12px; | ||||
|         } | ||||
|         span{ | ||||
|           font-size: 22px; | ||||
|           font-family: PingFang-SC-Medium, PingFang-SC; | ||||
|           font-weight: 500; | ||||
|           color: #999; | ||||
|           line-height: 32px; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										138
									
								
								src/pages/bigHorn/playList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,138 @@ | ||||
| <template> | ||||
|   <div class="playList"> | ||||
|     <div class="title"> | ||||
|       <span>播放记录</span> | ||||
|       <span>操作</span> | ||||
|     </div> | ||||
|     <div class="record"> | ||||
|       <div class="item" v-for="(item, index) in list" :key="index">  | ||||
|         <div class="info"> | ||||
|           <p>{{item.messageName}}</p> | ||||
|           <span>{{item.createTime}}</span><br /> | ||||
|           <span>{{item.deviceName}}</span> | ||||
|         </div> | ||||
|         <div class="btn bg-3975C6" v-if="item.broadcastStatus == 0 || item.broadcastStatus == 1 || item.broadcastStatus == 2" @click="cancel(item.broadcastId)">撤销</div> | ||||
|         <div class="btn bg-AFD0FC" v-if="item.broadcastStatus == 6">已取消</div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <AiBack></AiBack> | ||||
|   </div> | ||||
| </template> | ||||
| <script>  | ||||
|   import AiBack from "@/components/AiBack"; | ||||
| export default { | ||||
|   name: "playList", | ||||
|   data() { | ||||
|     return { | ||||
|       page: {current: 1, size: 10, total: 0}, | ||||
|       list: [] | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   components: { | ||||
|     AiBack | ||||
|   }, | ||||
|   methods: { | ||||
|     getList() { | ||||
|       this.$http.post("/app/appzyvideobroadcast/getBroadcastRecords", null, { | ||||
|         params: {...this.page} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           if (this.page.current > 1) { | ||||
|             this.list = [...this.list, ...res.data.records] | ||||
|           } else this.list = res.data.records | ||||
|           this.page.total = res.data.total | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     reachBottom() { | ||||
|       if (this.page.total > this.list.length) { | ||||
|         this.page.current++ | ||||
|         this.getList() | ||||
|       } | ||||
|     }, | ||||
|     cancel(id) { | ||||
|       this.$confirm('确定撤回该广播?').then(() => { | ||||
|         this.$http.post(`/app/appzyvideobroadcast/getBroadcastRecall?broadcastId=${id}`).then((res) => { | ||||
|           if (res.code == 0) { | ||||
|             this.$u.toast('撤回成功!') | ||||
|             this.getList() | ||||
|           } | ||||
|         }) | ||||
|       }) | ||||
|     }, | ||||
|   }, | ||||
|   created() { | ||||
|     this.$dict.load('dlbBroadcastStatus').then(() => { | ||||
|       this.getList() | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .playList { | ||||
|   .title{ | ||||
|     width: 100%; | ||||
|     height: 88px; | ||||
|     line-height: 88px; | ||||
|     background: #FFF; | ||||
|     padding: 0 80px; | ||||
|     display: flex; | ||||
|     justify-content: space-between; | ||||
|     color: #333; | ||||
|     font-size: 34px; | ||||
|     box-sizing: border-box; | ||||
|     border-bottom: 1px solid #ddd; | ||||
|   } | ||||
|   .record{ | ||||
|     padding-left: 32px; | ||||
|     background-color: #fff; | ||||
|     .item{ | ||||
|       width: 100%; | ||||
|       border-bottom: 1px solid #ddd; | ||||
|       display: flex; | ||||
|       justify-content: space-between; | ||||
|       padding: 12px 40px 16px 0; | ||||
|       box-sizing: border-box; | ||||
|       .info{ | ||||
|         width: 480px; | ||||
|         margin-right: 40px; | ||||
|         word-break: break-all; | ||||
|         p{ | ||||
|           font-size: 34px; | ||||
|           font-family: PingFang-SC-Medium, PingFang-SC; | ||||
|           font-weight: 500; | ||||
|           color: #333; | ||||
|           line-height: 48px; | ||||
|           margin-bottom: 12px; | ||||
|         } | ||||
|         span{ | ||||
|           font-size: 22px; | ||||
|           font-family: PingFang-SC-Medium, PingFang-SC; | ||||
|           font-weight: 500; | ||||
|           color: #999; | ||||
|           line-height: 32px; | ||||
|         } | ||||
|       } | ||||
|       .btn{ | ||||
|         width: 154px; | ||||
|         height: 60px; | ||||
|         border-radius: 8px; | ||||
|         text-align: center; | ||||
|         line-height: 60px; | ||||
|         font-size: 30px; | ||||
|         font-family: PingFang-SC-Medium, PingFang-SC; | ||||
|         font-weight: 500; | ||||
|         color: #FFF; | ||||
|         margin-top: 18px; | ||||
|       } | ||||
|       .bg-3975C6{ | ||||
|         background: #3975C6; | ||||
|       } | ||||
|       .bg-AFD0FC{ | ||||
|         background: #AFD0FC; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										160
									
								
								src/pages/bigHorn/selectEquipment.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,160 @@ | ||||
| <template> | ||||
|   <div class="selectEquipment"> | ||||
|     <div class="search"> | ||||
|       <div class="search-bg"> | ||||
|         <img src="./img/search-icon.png" alt=""> | ||||
|         <u-input v-model="value" type="text" placeholder="搜索设备名称" class="search-input" height="18" /> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="record"> | ||||
|       <div class="item"> | ||||
|         <img src="./img/cir.png" alt="" class="check-img"> | ||||
|         <img src="./img/lb@2x.png" alt="" class="voice-img"> | ||||
|         <div class="info"> | ||||
|           <div class="text"> | ||||
|             <p>村头大喇叭</p> | ||||
|             <span>刘家河居委会</span> | ||||
|           </div> | ||||
|           <div class="status">在线</div> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="item"> | ||||
|         <img src="./img/cir.png" alt="" class="check-img"> | ||||
|         <img src="./img/lb@2x.png" alt="" class="voice-img"> | ||||
|         <div class="info"> | ||||
|           <div class="text"> | ||||
|             <p>村头大喇叭</p> | ||||
|             <span>刘家河居委会</span> | ||||
|           </div> | ||||
|           <div class="status">在线</div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="btn"> | ||||
|       <div>确定选择</div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| <script>  | ||||
| export default { | ||||
|   name: "selectEquipment", | ||||
|   data() { | ||||
|     return { | ||||
|       value: '' | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|      | ||||
|   }, | ||||
|   mounted() { | ||||
|  | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .selectEquipment { | ||||
|   padding-bottom: 128px; | ||||
|   .search{ | ||||
|     width: 100%; | ||||
|     height: 104px; | ||||
|     background: #FFF; | ||||
|     margin-bottom: 4px; | ||||
|     padding: 20px 32px; | ||||
|     box-sizing: border-box; | ||||
|     .search-bg{ | ||||
|       width: 686px; | ||||
|       height: 64px; | ||||
|       padding: 14px 0; | ||||
|       box-sizing: border-box; | ||||
|       background: #F5F5F5; | ||||
|       border-radius: 32px; | ||||
|       position: relative; | ||||
|       img{ | ||||
|         width: 32px; | ||||
|         height: 32px; | ||||
|         position: absolute; | ||||
|         top: 16px; | ||||
|         left: 32px; | ||||
|       } | ||||
|       .search-input{ | ||||
|         width: 590px; | ||||
|         height: 36px; | ||||
|         line-height: 36px; | ||||
|         font-size: 28px; | ||||
|         margin-left: 70px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   .record{ | ||||
|     padding-left: 32px; | ||||
|     background-color: #fff; | ||||
|     .item{ | ||||
|       width: 100%; | ||||
|       display: flex; | ||||
|       border-bottom: 1px solid #ddd; | ||||
|       .check-img{ | ||||
|         width: 32px; | ||||
|         height: 32px; | ||||
|         margin: 32px 32px 0 0; | ||||
|       } | ||||
|       .voice-img{ | ||||
|         width: 48px; | ||||
|         height: 48px; | ||||
|         margin: 28px 16px 0 0; | ||||
|       } | ||||
|       .info{ | ||||
|         width: calc(100% - 148px); | ||||
|         padding: 18px 0; | ||||
|         line-height: 44px; | ||||
|         font-size: 34px; | ||||
|         display: flex; | ||||
|         justify-content: space-between; | ||||
|         .text{ | ||||
|           p{ | ||||
|             font-family: PingFang-SC-Medium, PingFang-SC; | ||||
|             font-weight: 500; | ||||
|             color: #333; | ||||
|             margin-bottom: 8px; | ||||
|           } | ||||
|           span{ | ||||
|             font-size: 26px; | ||||
|             font-family: PingFang-SC-Medium, PingFang-SC; | ||||
|             font-weight: 500; | ||||
|             color: #999; | ||||
|             line-height: 36px; | ||||
|           } | ||||
|         } | ||||
|         .status{ | ||||
|           font-size: 34px; | ||||
|           font-family: PingFang-SC-Medium, PingFang-SC; | ||||
|           font-weight: 500; | ||||
|           color: #4E8EEE; | ||||
|           line-height: 48px; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   .btn{ | ||||
|     position: fixed; | ||||
|     bottom: 0; | ||||
|     left: 0; | ||||
|     width: 100%; | ||||
|     height: 128px; | ||||
|     background: #FFF; | ||||
|     border-top: 1px solid #ddd; | ||||
|     padding: 24px 32px 24px 0; | ||||
|     box-sizing: border-box; | ||||
|     div{ | ||||
|       width: 192px; | ||||
|       height: 80px; | ||||
|       line-height: 80px; | ||||
|       text-align: center; | ||||
|       background: #3975C6; | ||||
|       border-radius: 4px; | ||||
|       color: #fff; | ||||
|       font-size: 32px; | ||||
|       float: right; | ||||
|     }  | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										83
									
								
								src/pages/bigHorn/selectMp3.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,83 @@ | ||||
| <template> | ||||
|   <div class="selectMp3"> | ||||
|     <div class="record"> | ||||
|       <div class="item"> | ||||
|         <img src="./img/cir.png" alt=""> | ||||
|         <div class="info"> | ||||
|           <p>村头大喇叭</p> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="btn"> | ||||
|       <div>确定选择</div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| <script>  | ||||
| export default { | ||||
|   name: "selectMp3", | ||||
|   data() { | ||||
|     return { | ||||
|        | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|      | ||||
|   }, | ||||
|   mounted() { | ||||
|  | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .selectMp3 { | ||||
|   padding-bottom: 128px; | ||||
|   .record{ | ||||
|     padding-left: 32px; | ||||
|     background-color: #fff; | ||||
|     .item{ | ||||
|       width: 100%; | ||||
|       display: flex; | ||||
|       img{ | ||||
|         width: 32px; | ||||
|         height: 32px; | ||||
|         margin: 40px 16px 0 0; | ||||
|       } | ||||
|       .info{ | ||||
|         width: calc(100% - 60px); | ||||
|         padding-bottom: 16px; | ||||
|         padding: 34px 0; | ||||
|         line-height: 44px; | ||||
|         border-bottom: 1px solid #ddd; | ||||
|         font-size: 34px; | ||||
|         font-family: PingFang-SC-Medium, PingFang-SC; | ||||
|         font-weight: 500; | ||||
|         color: #333; | ||||
|         margin-left: 16px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   .btn{ | ||||
|     position: fixed; | ||||
|     bottom: 0; | ||||
|     left: 0; | ||||
|     width: 100%; | ||||
|     height: 128px; | ||||
|     background: #FFF; | ||||
|     border-top: 1px solid #ddd; | ||||
|     padding: 24px 32px 24px 0; | ||||
|     box-sizing: border-box; | ||||
|     div{ | ||||
|       width: 192px; | ||||
|       height: 80px; | ||||
|       line-height: 80px; | ||||
|       text-align: center; | ||||
|       background: #3975C6; | ||||
|       border-radius: 4px; | ||||
|       color: #fff; | ||||
|       font-size: 32px; | ||||
|       float: right; | ||||
|     }  | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										268
									
								
								src/pages/casuallyask/casuallyask.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,268 @@ | ||||
| <template> | ||||
|   <div class="casuallyask"> | ||||
|     <div class="banner"> | ||||
|       <picker :range="dictList" @change="bindPickerChange" range-key="dictName"> | ||||
|         <div class="picker"> | ||||
|           {{ askType }} | ||||
|           <u-icon | ||||
|               name="arrow-down" | ||||
|               :custom-style="{ margin: '0 5px' }" | ||||
|           ></u-icon> | ||||
|         </div> | ||||
|       </picker> | ||||
|       <u-search | ||||
|           placeholder="请输入标题" | ||||
|           :show-action="false" | ||||
|           v-model="keyword" | ||||
|           @search="onSearch" | ||||
|       /> | ||||
|     </div> | ||||
|     <u-tabs | ||||
|         class="nav" | ||||
|         :list="tabs" | ||||
|         :is-scroll="false" | ||||
|         :current="currentType" | ||||
|         font-size="32" | ||||
|         bar-width="192" | ||||
|         height="96" | ||||
|         @change="handleTabClick" | ||||
|     ></u-tabs> | ||||
|     <div class="body" v-if="eventList.length !== 0"> | ||||
|       <div | ||||
|           class="content" | ||||
|           v-for="(item, index) in eventList" | ||||
|           :key="index" | ||||
|           @click="detail(item)" | ||||
|       > | ||||
|         <u-row> | ||||
|           <div> | ||||
|             <div class="top"> | ||||
|  | ||||
|               <text :class="item.type == '0' ? 'active' : 'noactive'"> | ||||
|                 {{ $dict.getLabel('leaveMessageType', item.type) }} | ||||
|               </text> | ||||
|               <!-- 村 --> | ||||
|               <text class="areaName" v-if="item.areaName">{{ item.areaName }}:</text> | ||||
|               <!-- 问题 --> | ||||
|               <text class="title">{{ item.title }}</text> | ||||
|             </div> | ||||
|  | ||||
|             <div class="cont"> | ||||
|               <span class="name_time">留言人:</span> | ||||
|               <text class="text_c"> {{ item.leaveName }}</text> | ||||
|             </div> | ||||
|             <div class="cont"> | ||||
|               <text class="name_time">留言时间:</text> | ||||
|               <text class="text_c"> {{ item.createTime }}</text> | ||||
|             </div> | ||||
|           </div> | ||||
|         </u-row> | ||||
|       </div> | ||||
|     </div> | ||||
|     <AiEmpty v-else/> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import AiEmpty from '../../components/AiEmpty/AiEmpty' | ||||
| import URow from '../../uview/components/u-row/u-row.vue' | ||||
|  | ||||
| export default { | ||||
|   name: 'casuallyAsk', | ||||
|   // 组件 | ||||
|   components: {URow, AiEmpty}, | ||||
|   props: {}, | ||||
|   data() { | ||||
|     return { | ||||
|       page: {current: 1, size: 10, total: 0}, | ||||
|       dictList: [], // 字典 | ||||
|       index: 0, // 留言类型 0投诉 1咨询 2建议 | ||||
|       keyword: '', | ||||
|       askType: '提问类型', | ||||
|       askIndex: '', | ||||
|       currentType: 0, // 默认value的值(0待我回复)(1已回复)(2处理完成) | ||||
|       eventList: [] // 数据列表 | ||||
|       // temp: { '0': '投诉', '1': '建议', '2': '咨询' } | ||||
|     } | ||||
|   }, | ||||
|   // 计算 | ||||
|   computed: { | ||||
|     tabs() { | ||||
|       return [ | ||||
|         {name: '待我回复', value: 0}, | ||||
|         {name: '我已回复', value: 1}, | ||||
|         {name: '处理完成', value: 2} | ||||
|       ] | ||||
|     } | ||||
|   }, | ||||
|   // 实例创建后 | ||||
|   onShow() { | ||||
|     this.$dict.load('leaveMessageType').then(() => { | ||||
|       this.dictList = this.$dict.getDict('leaveMessageType') | ||||
|       this.getList() | ||||
|     }) | ||||
|   }, | ||||
|   // 方法 | ||||
|   methods: { | ||||
|     // 点击 value的值(0待我回复)(1已回复)(2处理完成) | ||||
|     handleTabClick(i) { | ||||
|       this.currentType = i | ||||
|       this.getList() | ||||
|     }, | ||||
|     getList() { | ||||
|       this.$http.post(`/app/appleavemessage/list`, null, { | ||||
|         params: { | ||||
|           ...this.page, | ||||
|           title: this.keyword, | ||||
|           status: this.currentType, | ||||
|           type: this.askIndex | ||||
|         } | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.page.total = res.data.total | ||||
|           if (this.page.current > 1) | ||||
|             this.eventList = [...this.eventList, res.data.records] | ||||
|           else this.eventList = res.data.records | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     detail(item) { | ||||
|       uni.navigateTo({ | ||||
|         url: `/pages/casuallyask/casuallyaskDetail?id=${item.id}&type=${item.type}` | ||||
|         //  url: `/pages/casuallyask/casuallyaskDetail?id=${item.id}` | ||||
|       }) | ||||
|     }, | ||||
|     // 提问类型 | ||||
|     bindPickerChange(e) { | ||||
|       let index = e.detail.value | ||||
|       this.askType = this.dictList[index].dictName | ||||
|       this.askIndex = index | ||||
|       this.getList() | ||||
|     }, | ||||
|     onSearch(e) { | ||||
|       this.keyword = e | ||||
|       this.getList() | ||||
|     } | ||||
|   }, | ||||
|   onReachBottom() { | ||||
|     if (this.eventList.length < this.page.total) { | ||||
|       this.page.current++ | ||||
|       this.getList() | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| .casuallyask { | ||||
|   min-height: 100%; | ||||
|   background: #f5f5f5; | ||||
|   padding-top: 64px; | ||||
|  | ||||
|   .banner { | ||||
|     position: fixed; | ||||
|     top: 0; | ||||
|     width: 100%; | ||||
|     background-color: #fff; | ||||
|     height: 80px; | ||||
|     line-height: 80px; | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     justify-content: space-between; | ||||
|     box-sizing: border-box; | ||||
|     padding: 0 30px 0 30px; | ||||
|  | ||||
|     .picker { | ||||
|       font-size: 30px; | ||||
|       font-weight: 500; | ||||
|       color: #333333; | ||||
|       margin-right: 8px; | ||||
|  | ||||
|       .u-icon-wrap { | ||||
|         margin-left: 25px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .nav { | ||||
|     position: fixed; | ||||
|     top: 80px; | ||||
|     background-color: #ffffff; | ||||
|     height: 96px; | ||||
|     padding-top: 8px; | ||||
|     width: 100%; | ||||
|   } | ||||
|  | ||||
|   .body { | ||||
|     padding: 114px 0 0 0; | ||||
|  | ||||
|     .content { | ||||
|       background-color: #ffffff; | ||||
|       box-sizing: border-box; | ||||
|       padding: 34px 32px; | ||||
|       margin: 32px 20px; | ||||
|  | ||||
|       .top { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         width: 100%; | ||||
|  | ||||
|         .typeBox { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         .noactive { | ||||
|           text-align: center; | ||||
|           color: #2266ff; | ||||
|           width: 70px; | ||||
|           height: 44px; | ||||
|           line-height: 44px; | ||||
|           border-radius: 8px; | ||||
|           background-color: #e8efff; | ||||
|         } | ||||
|  | ||||
|         .active { | ||||
|           text-align: center; | ||||
|           color: #ff4466; | ||||
|           line-height: 44px; | ||||
|           width: 70px; | ||||
|           height: 44px; | ||||
|           border-radius: 8px; | ||||
|           background-color: #ffebef; | ||||
|         } | ||||
|  | ||||
|         .areaName { | ||||
|           display: inline-block; | ||||
|           color: #333; | ||||
|           font-size: 32px; | ||||
|           margin-left: 15px; | ||||
|         } | ||||
|  | ||||
|         .title { | ||||
|           color: #333; | ||||
|           font-size: 32px; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .cont { | ||||
|         margin: 10px 0; | ||||
|  | ||||
|         .name_time { | ||||
|           display: inline-block; | ||||
|           width: 150px; | ||||
|           height: 24px; | ||||
|           color: #999; | ||||
|           font-size: 30px; | ||||
|           // font-weight: 800; | ||||
|         } | ||||
|  | ||||
|         .text_c { | ||||
|           color: #343d65; | ||||
|           font-size: 30px; | ||||
|           // font-weight: 800; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										442
									
								
								src/pages/casuallyask/casuallyaskDetail.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,442 @@ | ||||
| <template> | ||||
|   <div class="casuallyaskDetail"> | ||||
|     <div class="detail"> | ||||
|       <div class="headerTitle" flex> | ||||
|         <div class="temp"> | ||||
|           [{{ $dict.getLabel('leaveMessageType', detail.type) }}] | ||||
|         </div> | ||||
|         <span class="areaName">{{ detail.areaName }}</span> | ||||
|         <span>{{ detail.title }}</span> | ||||
|       </div> | ||||
|       <div class="leaveName_leavePhone"> | ||||
|         <!-- 顶部圆形头像 --> | ||||
|         <span class="icon">{{ detail.headPortrait || $formatName(detail.leaveName) }}</span> | ||||
|         <span class="leaveName">{{ detail.leaveName }}</span> | ||||
|         <span class="leavePhone" v-if="detail.leavePhone">({{ detail.leavePhone }})</span> | ||||
|       </div> | ||||
|       <!-- 编号 --> | ||||
|       <div class="info-item"> | ||||
|         <span class="label"> | ||||
|           <u-icon name="order"></u-icon> | ||||
|         </span> | ||||
|         <span class="value">{{ detail.msgCode }}</span> | ||||
|       </div> | ||||
|       <!-- 日期 --> | ||||
|       <div class="info-item"> | ||||
|         <span class="label"> | ||||
|           <u-icon name="clock"></u-icon> | ||||
|         </span> | ||||
|         <span class="value">{{ detail.createTime }}</span> | ||||
|       </div> | ||||
|       <!-- 进度--> | ||||
|       <div class="info-item"> | ||||
|         <span class="label"><u-icon name="tags"/></span> | ||||
|         <text class="status">{{ | ||||
|             $dict.getLabel('leaveMessageStatus', detail.status) | ||||
|           }} | ||||
|         </text> | ||||
|       </div> | ||||
|       <div class="content_text_img"> | ||||
|         <!-- 提问内容 --> | ||||
|         <div class="content_text"> | ||||
|           {{ detail.content }} | ||||
|         </div> | ||||
|         <!-- 提问内容的图片 --> | ||||
|         <div class="imageList"> | ||||
|           <ai-image preview :src="items.url" alt="" v-for="(items, i) in imgList" :key="i"/> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="reply_content" v-if="detail.status == 1 || detail.status == 2"> | ||||
|         <div class="reply_title"> | ||||
|           <img src="https://cdn.cunwuyun.cn/img/dialogue.svg" alt=""/> | ||||
|           <p>沟通记录</p> | ||||
|         </div> | ||||
|         <div class="reply_list"> | ||||
|           <div class="item" v-for="(item, index) in appLeaveMessageReplyList" :key="index"> | ||||
|             <div class="item_top"> | ||||
|               <div class="item_left"> | ||||
|                 <div class="icon"> | ||||
|                   {{ detail.headPortrait || $formatName(item.createUserName) }} | ||||
|                 </div> | ||||
|                 <div class="name fill"> | ||||
|                   <div class="createUserName_createUnitName"> | ||||
|                     <span v-if="item.createUserId == user.id" class="reply_font">我的回复</span> | ||||
|                     <template v-else> | ||||
|                       <span class="createUserName">{{ item.createUserName }} </span> | ||||
|                       <span class="createUserName" v-if="item.createUserPhone">({{ item.createUserPhone }})</span> | ||||
|                     </template> | ||||
|                   </div> | ||||
|                   <div flex v-if="item.createUserId != user.id"> | ||||
|                     <!-- 回复单位 --> | ||||
|                     <span class="createUnitName">{{ item.createUnitName }}</span> | ||||
|                     <span class="reply">回复</span> | ||||
|                   </div> | ||||
|                 </div> | ||||
|               </div> | ||||
|               <div class="item_right">{{ item.createTime }}</div> | ||||
|             </div> | ||||
|             <!-- 回复内容 --> | ||||
|             <div class="myreply_con"> | ||||
|               <div class="myreply_text"> | ||||
|                 {{ item.content }} | ||||
|               </div> | ||||
|               <!-- 回复图片 --> | ||||
|               <div class="imageList"> | ||||
|                 <ai-image v-for="img in item.images" :key="img.id" preview :src="img.url"/> | ||||
|               </div> | ||||
|               <!--  --> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="close_reply" v-if="detail.status == 0 || detail.status == 1"> | ||||
|       <div class="btn" @click="close">关闭留言</div> | ||||
|       <div class="btn reply" @click="reply">回复</div> | ||||
|     </div> | ||||
|     <u-modal | ||||
|         v-model="show" | ||||
|         :content="content" | ||||
|         cancel-color="#2979ff" | ||||
|         title="" | ||||
|         :async-close="true" | ||||
|         :show-cancel-button="true" | ||||
|         @confirm="handleCloseMessage" | ||||
|         @cancel="show = false"/> | ||||
|     <back/> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import back from '../../components/AiBack' | ||||
| import {mapState} from 'vuex' | ||||
| import AiImage from "../../components/AiImage"; | ||||
| import UImage from "../../uview/components/u-image/u-image"; | ||||
|  | ||||
| export default { | ||||
|   name: 'casuallyaskDetail', | ||||
|   components: {UImage, AiImage, back}, | ||||
|   props: {}, | ||||
|   computed: { | ||||
|     ...mapState(['user']) | ||||
|   }, | ||||
|   onLoad(options) { | ||||
|     this.getDetail(options.id) | ||||
|   }, | ||||
|   onShow() { | ||||
|     this.$dict.load('leaveMessageStatus', 'leaveMessageType') | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       objdie: '', // id | ||||
|       appLeaveMessageReplyList: [], //遍历得到的数据 | ||||
|       show: false, // 默认关闭模态框 | ||||
|       content: '关闭留言后,双方都无法再进行回复,是否确定关闭本次留言?', | ||||
|       msgCode: null, | ||||
|       imgList: [], | ||||
|       detail: {} | ||||
|       // images:[] | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getDetail(id = this.detail.id) { | ||||
|       this.$http.post(`/app/appleavemessage/queryDetailById?id=${id}`,).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.detail = res.data | ||||
|           this.imgList = JSON.parse(this.detail.images) | ||||
|           this.appLeaveMessageReplyList = res.data.appLeaveMessageReplyList.map(item => ({ | ||||
|             ...item, | ||||
|             images: JSON.parse(item.images).map(e => ({url: e.url || e?.file?.accessUrl, id: e.file?.id})) | ||||
|           })) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     // 关闭留言 | ||||
|     close() { | ||||
|       this.show = true | ||||
|       this.getDetail() | ||||
|     }, | ||||
|     // 确定关闭留言 | ||||
|     handleCloseMessage() { | ||||
|       this.$http.post(`/app/appleavemessage/release?id=${this.objdie}&status=2`).then(res => { | ||||
|         if (res?.code == 0) { | ||||
|           this.getDetail() | ||||
|           uni.navigateTo({url: `./closemsg?flag=true`}) | ||||
|         } | ||||
|       }).catch(() => { | ||||
|         uni.navigateTo({url: `./closemsg?flag=false`}) | ||||
|       }) | ||||
|       this.show = false | ||||
|     }, | ||||
|     // 去回复 | ||||
|     reply() { | ||||
|       uni.navigateTo({url: `./reply?msgCode=${this.detail.msgCode}`}) | ||||
|     } | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .casuallyaskDetail { | ||||
|   padding-bottom: 80px; | ||||
|  | ||||
|   .detail { | ||||
|     padding: 15px 32px 112px 32px; | ||||
|     background-color: #fff; | ||||
|     font-size: 30px; | ||||
|     font-weight: 400; | ||||
|     color: #343D65; | ||||
|  | ||||
|     .headerTitle { | ||||
|       font-size: 40px; | ||||
|       font-weight: bold; | ||||
|       color: #333; | ||||
|       line-height: 64px; | ||||
|       letter-spacing: 2px; | ||||
|       flex-wrap: wrap; | ||||
|     } | ||||
|  | ||||
|     .leaveName_leavePhone { | ||||
|       height: 56px; | ||||
|       margin: 20px 0 36px 0; | ||||
|  | ||||
|       .icon { | ||||
|         display: inline-block; | ||||
|         width: 56px; | ||||
|         height: 56px; | ||||
|         line-height: 56px; | ||||
|         border-radius: 50%; | ||||
|         background-color: #2266ff; | ||||
|         color: #fff; | ||||
|         font-size: 22px; | ||||
|         vertical-align: middle; | ||||
|         text-align: center; | ||||
|       } | ||||
|  | ||||
|       .leaveName { | ||||
|         // display: block; | ||||
|         margin-left: 20px; | ||||
|         color: #343d65; | ||||
|       } | ||||
|  | ||||
|       // .leavePhone { | ||||
|       // } | ||||
|     } | ||||
|  | ||||
|     .info-item { | ||||
|       height: 40px; | ||||
|       line-height: 40px; | ||||
|       font-size: 28px; | ||||
|       margin-bottom: 8px; | ||||
|  | ||||
|       .label { | ||||
|         display: inline-block; | ||||
|       } | ||||
|  | ||||
|       .value { | ||||
|         display: inline-block; | ||||
|         margin-left: 20px; | ||||
|       } | ||||
|  | ||||
|       .status { | ||||
|         display: inline-block; | ||||
|         margin-left: 20px; | ||||
|         // color: #999; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .content_text_img { | ||||
|       margin-top: 64px; | ||||
|       max-height: 700px; | ||||
|       color: #000; | ||||
|       font-size: 32px; | ||||
|       // font-weight: 800; | ||||
|       overflow: hidden; | ||||
|  | ||||
|       .content_text { | ||||
|         width: 100%; | ||||
|         margin-bottom: 12px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .reply_content { | ||||
|       margin-top: 25px; | ||||
|  | ||||
|       .reply_title { | ||||
|         margin-top: 25px; | ||||
|         width: 225px; | ||||
|         height: 45px; | ||||
|         line-height: 45px; | ||||
|  | ||||
|         img { | ||||
|           vertical-align: middle; | ||||
|         } | ||||
|  | ||||
|         p { | ||||
|           display: inline-block; | ||||
|           color: #333; | ||||
|           // font-weight: 800; | ||||
|           margin-left: 10px; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .reply_list { | ||||
|         .item { | ||||
|           .item_top { | ||||
|             display: flex; | ||||
|             justify-content: space-between; | ||||
|             margin-top: 10px; | ||||
|  | ||||
|             .item_left { | ||||
|               display: flex; | ||||
|               align-items: center; | ||||
|               flex: 1; | ||||
|               min-width: 0; | ||||
|  | ||||
|               .icon { | ||||
|                 display: block; | ||||
|                 flex-shrink: 0; | ||||
|                 width: 64px; | ||||
|                 height: 64px; | ||||
|                 line-height: 64px; | ||||
|                 border-radius: 50%; | ||||
|                 background-color: #2266ff; | ||||
|                 color: #fff; | ||||
|                 font-size: 23px; | ||||
|                 text-align: center; | ||||
|                 margin-right: 10px; | ||||
|               } | ||||
|  | ||||
|               .name { | ||||
|                 display: flex; | ||||
|                 flex-direction: column; | ||||
|  | ||||
|                 .createUnitName { | ||||
|                   color: #135ab8; | ||||
|                   margin-right: 8px; | ||||
|                 } | ||||
|  | ||||
|                 .createUserName_createUnitName { | ||||
|                   width: 100%; | ||||
|                   display: flex; | ||||
|  | ||||
|                   .reply_font { | ||||
|                     width: 120px; | ||||
|                     color: #333333; | ||||
|                     font-size: 28px; | ||||
|                   } | ||||
|  | ||||
|                   .createUserName { | ||||
|                     margin-bottom: 5px; | ||||
|                     color: #135ab8; | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|  | ||||
|             .item_right { | ||||
|               font-size: 26px; | ||||
|               color: #999; | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           .myreply_con { | ||||
|             width: 570px; | ||||
|             background-color: #f3f6f9; | ||||
|             margin: 24px 0 0 80px; | ||||
|             padding: 14px; | ||||
|  | ||||
|             .myreply_text { | ||||
|               overflow: auto; | ||||
|               word-wrap: break-word; | ||||
|               word-break: break-all; | ||||
|             } | ||||
|  | ||||
|             .imageList { | ||||
|               display: flex; | ||||
|               flex-wrap: wrap; | ||||
|  | ||||
|               .AiImage { | ||||
|                 margin: 0 12px 12px 0; | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         .no_yes_reply { | ||||
|           .con_title { | ||||
|             display: flex; | ||||
|             flex-direction: row; | ||||
|             justify-content: space-between; | ||||
|             margin-top: 16px; | ||||
|             height: 64px; | ||||
|             line-height: 64px; | ||||
|  | ||||
|             .icon { | ||||
|               display: inline-block; | ||||
|               width: 60px; | ||||
|               height: 60px; | ||||
|               line-height: 60px; | ||||
|               border-radius: 50%; | ||||
|               background-color: #2266ff; | ||||
|               color: #fff; | ||||
|               font-size: 23px; | ||||
|               vertical-align: middle; | ||||
|               text-align: center; | ||||
|               // font-weight: 800; | ||||
|             } | ||||
|  | ||||
|             .my_reply { | ||||
|               width: 370px; | ||||
|             } | ||||
|  | ||||
|             .right { | ||||
|               color: #999; | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           .myreply_content { | ||||
|             width: 606px; | ||||
|             height: 460px; | ||||
|             background-color: #f3f6f9; | ||||
|             margin: 24px 0 0 62px; | ||||
|             padding: 16px; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .no_more { | ||||
|       padding: 44px 0; | ||||
|       color: #999; | ||||
|       font-size: 24px; | ||||
|       text-align: center; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .close_reply { | ||||
|     position: fixed; | ||||
|     bottom: 0; | ||||
|     display: flex; | ||||
|     height: 112px; | ||||
|     width: 100%; | ||||
|     border-top: 1px solid #ddd; | ||||
|     z-index: 999; | ||||
|     background: #fff; | ||||
|  | ||||
|     .btn { | ||||
|       flex: 1; | ||||
|       min-width: 0; | ||||
|       background-color: #fff; | ||||
|       display: flex; | ||||
|       justify-content: center; | ||||
|       align-items: center; | ||||
|  | ||||
|       &.reply { | ||||
|         background-color: #1365dd; | ||||
|         color: #fff; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										78
									
								
								src/pages/casuallyask/closemsg.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,78 @@ | ||||
| <template> | ||||
|   <div class="closemsg"> | ||||
|     <img :src="imgSrc" alt="" /> | ||||
|     <text>{{ text }}</text> | ||||
|     <u-button | ||||
|       type="primary" | ||||
|       :custom-style="{ width: '100%', borderRadius: '4px', marginTop: '48px' }" | ||||
|       @click="goBack" | ||||
|       >{{ btnText }}</u-button | ||||
|     > | ||||
|     <back></back> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import back from '../../components/AiBack' | ||||
|  | ||||
| export default { | ||||
|   name: 'CloseMsg', | ||||
|   components: { back }, | ||||
|   data() { | ||||
|     return { | ||||
|       flag: true | ||||
|     } | ||||
|   }, | ||||
|   onLoad(val) { | ||||
|     if (val.flag) { | ||||
|       this.flag = val.flag | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     goBack() { | ||||
|       uni.navigateBack({ | ||||
|         // url: `/pages/casuallyask/casuallyask` | ||||
|         delta: 1 | ||||
|       }) | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     text() { | ||||
|       return this.flag ? '关闭留言成功!' : '关闭留言失败' | ||||
|     }, | ||||
|     btnText() { | ||||
|       return this.flag ? '确定' : '查看详情' | ||||
|     }, | ||||
|     imgSrc() { | ||||
|       return this.flag | ||||
|         ? this.imgOtherUrl + 'kztcg.png' | ||||
|         : this.imgOtherUrl + 'kztsb.png' | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .closemsg { | ||||
|   min-height: 100%; | ||||
|   background-color: #ffffff; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   align-items: center; | ||||
|   padding: 96px; | ||||
|  | ||||
|   img { | ||||
|     width: 192px; | ||||
|     height: 192px; | ||||
|   } | ||||
|  | ||||
|   text { | ||||
|     font-size: 36px; | ||||
|     font-weight: 800; | ||||
|     color: #333333; | ||||
|     line-height: 50px; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										197
									
								
								src/pages/casuallyask/reply.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,197 @@ | ||||
| <template> | ||||
|   <div class="reply"> | ||||
|     <div class="reply_content"> | ||||
|       <span class="icon">*</span> | ||||
|       <text class="msgfont">回复内容</text> | ||||
|       <textarea :maxlength="500" placeholder="请输入内容(500字以内)" v-model="msg" class="textarea"></textarea> | ||||
|       <!-- <u-input | ||||
|         type="textarea" | ||||
|         height="50" | ||||
|         :auto-height="false" | ||||
|         placeholder="请输入内容(500字以内)" | ||||
|         placeholder-style="color:#999;" | ||||
|         maxlength="500" | ||||
|         v-model="msg" | ||||
|         class="textarea" | ||||
|       /> --> | ||||
|     </div> | ||||
|     <div class="reply_img"> | ||||
|       <text class="img">图片资料</text> | ||||
|       <text class="img_text">(最多9张)</text> | ||||
|       <div class="upload"> | ||||
|         <div class="info"> | ||||
|           <ai-uploader multiple @data="data" @change="change" :limit="9" action="/admin/file/add2"></ai-uploader> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <ai-back/> | ||||
|     <div class="submit"> | ||||
|       <button class="btn" @click="btn">提交</button> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import AiUploader from '../../components/AiUploader' | ||||
| import AiBack from "../../components/AiBack"; | ||||
|  | ||||
| export default { | ||||
|   // name: '', | ||||
|   // 组件 | ||||
|   components: {AiBack, AiUploader}, | ||||
|   props: {}, | ||||
|   data() { | ||||
|     return { | ||||
|       msg: '', | ||||
|       files: [], | ||||
|       msgCode: '', | ||||
|     } | ||||
|   }, | ||||
|   onLoad(options) { | ||||
|     this.msgCode = options.msgCode | ||||
|   }, | ||||
|   // 计算 | ||||
|   computed: {}, | ||||
|   // 监听 | ||||
|   watch: {}, | ||||
|   // 实例创建后 | ||||
|   onShow() { | ||||
|     this.$dict.load('leaveMessageType').then(() => { | ||||
|       this.dictList = this.$dict.getDict('leaveMessageType') | ||||
|     }) | ||||
|   }, | ||||
|   // 实例渲染后 | ||||
|   mounted() { | ||||
|   }, | ||||
|   // 方法 | ||||
|   methods: { | ||||
|     data(e) { | ||||
|       this.files.push(e) | ||||
|     }, | ||||
|     // selectEventType(selecteds) { | ||||
|     //   this.eventType = selecteds?.[0]?.value | ||||
|     // }, | ||||
|     change(e) { | ||||
|       this.files = e | ||||
|     }, | ||||
|     btn() { | ||||
|       if (this.msg == '') { | ||||
|         return uni.showToast({ | ||||
|           title: '请输入留言内容', | ||||
|           icon: 'none', | ||||
|         }) | ||||
|       } | ||||
|       this.$http | ||||
|       .post(`/app/appleavemessagereply/addOrUpdate`, { | ||||
|         images: JSON.stringify(this.files), | ||||
|         content: this.msg, | ||||
|         msgCode: this.msgCode, | ||||
|         userType: '1', | ||||
|       }).then(res => { | ||||
|         if (res?.code == 0) { | ||||
|           uni.navigateTo({ | ||||
|             url: `/pages/casuallyask/truemsg?flag=1`, | ||||
|           }) | ||||
|         } | ||||
|       }).catch(err => { | ||||
|         uni.navigateTo({ | ||||
|           url: `/pages/casuallyask/truemsg?flag=0`, | ||||
|         }) | ||||
|         this.$u.toast(err || '网络异常') | ||||
|       }) | ||||
|     }, | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| .reply { | ||||
|   width: 100%; | ||||
|   // height: 100%; | ||||
|   .reply_content { | ||||
|     height: 288px; | ||||
|     padding: 0 20px 0 20px; | ||||
|     background-color: #fff; | ||||
|  | ||||
|     .icon { | ||||
|       display: inline-block; | ||||
|       width: 16px; | ||||
|       height: 44px; | ||||
|       line-height: 44px; | ||||
|       font-size: 42px; | ||||
|       vertical-align: middle; | ||||
|       color: red; | ||||
|       margin-top: 10px; | ||||
|     } | ||||
|  | ||||
|     .msgfont { | ||||
|       // overflow: auto; | ||||
|       // word-wrap: break-word; | ||||
|       // word-break: break-all; | ||||
|       display: inline-block; | ||||
|       width: 226px; | ||||
|       height: 44px; | ||||
|       margin-top: 34px; | ||||
|       line-height: 44px; | ||||
|       font-size: 32px; | ||||
|       // font-weight: 800; | ||||
|       margin-left: 10px; | ||||
|     } | ||||
|  | ||||
|     .textarea { | ||||
|       height: 190px; | ||||
|       width: 700px; | ||||
|       // overflow: auto; | ||||
|       // word-wrap: break-word; | ||||
|       // word-break: break-all; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .reply_img { | ||||
|     margin-top: 20px; | ||||
|     background-color: #fff; | ||||
|     padding: 0 20px; | ||||
|     box-sizing: border-box; | ||||
|  | ||||
|     .img { | ||||
|       display: inline-block; | ||||
|       margin-top: 20px; | ||||
|       font-size: 32px; | ||||
|       // font-weight: 800; | ||||
|     } | ||||
|  | ||||
|     .img_text { | ||||
|       font-size: 32px; | ||||
|       color: #999; | ||||
|       margin-left: 10px; | ||||
|     } | ||||
|  | ||||
|     .upload { | ||||
|       width: 100%; | ||||
|       margin-top: 10px; | ||||
|       padding: 12px 12px 12px 0; | ||||
|       box-sizing: border-box; | ||||
|  | ||||
|       .info { | ||||
|         width: 100%; | ||||
|         text-align: center; | ||||
|       } | ||||
|  | ||||
|       ::v-deep .ai-uploader .fileList .default { | ||||
|         width: 160px; | ||||
|         height: 160px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .submit { | ||||
|     padding: 50px 32px; | ||||
|  | ||||
|     .btn { | ||||
|       background-color: #1365dd; | ||||
|       color: #fff; | ||||
|       font-size: 36px; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										75
									
								
								src/pages/casuallyask/truemsg.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,75 @@ | ||||
| <template> | ||||
|   <div class="closemsg"> | ||||
|     <img :src="imgSrc" alt=""/> | ||||
|     <text>{{ text }}</text> | ||||
|     <u-button | ||||
|         type="primary" | ||||
|         :custom-style="{ width: '100%', borderRadius: '4px', marginTop: '48px' }" | ||||
|         @click="goBack" | ||||
|     >{{ btnText }} | ||||
|     </u-button> | ||||
|     <back></back> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import back from '../../components/AiBack' | ||||
|  | ||||
| export default { | ||||
|   name: 'CloseMsg', | ||||
|   components: {back}, | ||||
|   data() { | ||||
|     return { | ||||
|       flag: true | ||||
|     } | ||||
|   }, | ||||
|   onLoad(val) { | ||||
|     this.flag = val?.flag == 1 | ||||
|   }, | ||||
|   methods: { | ||||
|     goBack() { | ||||
|       uni.navigateBack({ | ||||
|         delta: 2 | ||||
|       }) | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     text() { | ||||
|       return this.flag ? '提交成功!' : '处理失败' | ||||
|     }, | ||||
|     btnText() { | ||||
|       return this.flag ? '查看详情' : '我知道了' | ||||
|     }, | ||||
|     imgSrc() { | ||||
|       return this.flag | ||||
|           ? this.imgOtherUrl + 'kztcg.png' | ||||
|           : this.imgOtherUrl + 'kztsb.png' | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .closemsg { | ||||
|   min-height: 100%; | ||||
|   background-color: #ffffff; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   align-items: center; | ||||
|   padding: 96px; | ||||
|  | ||||
|   img { | ||||
|     width: 192px; | ||||
|     height: 192px; | ||||
|   } | ||||
|  | ||||
|   text { | ||||
|     font-size: 36px; | ||||
|     font-weight: 800; | ||||
|     color: #333333; | ||||
|     line-height: 50px; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										91
									
								
								src/pages/documentFlow/components/approval.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,91 @@ | ||||
| <template> | ||||
|   <div class="approval"> | ||||
|     <div class="card"> | ||||
|       <header>批示意见</header> | ||||
|       <textarea placeholder="请输入批示意见" v-model.trim="description" maxlength="200"></textarea> | ||||
|       <u-row justify="between"> | ||||
|         <span @click="description=''">清空内容</span> | ||||
|         <span>{{description.length || 0}}/200</span> | ||||
|       </u-row> | ||||
|     </div> | ||||
|     <ai-back/> | ||||
|     <u-button type="primary" @click="submit">提交</u-button> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import AiBack from "../../../components/AiBack"; | ||||
|  | ||||
|   export default { | ||||
|     name: "approval", | ||||
|     components: {AiBack}, | ||||
|     data() { | ||||
|       return { | ||||
|         id: null, | ||||
|         description: "" | ||||
|       } | ||||
|     }, | ||||
|     onLoad(opt) { | ||||
|       this.id = opt.id | ||||
|     }, | ||||
|     methods: { | ||||
|       submit() { | ||||
|         this.$http.post("/app/appofficialdocumentinfo/instructionById", null, { | ||||
|           params: { | ||||
|             id: this.id, | ||||
|             description: this.description | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.$u.toast("批示成功") | ||||
|             uni.navigateBack() | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .approval { | ||||
|     background: #F5F5F5; | ||||
|  | ||||
|     .card { | ||||
|       background-color: #FFFFFF; | ||||
|       box-sizing: border-box; | ||||
|       padding: 32px; | ||||
|  | ||||
|       header { | ||||
|         font-size: 32px; | ||||
|         color: #333333; | ||||
|         margin-bottom: 16px; | ||||
|         font-weight: bold; | ||||
|       } | ||||
|  | ||||
|       textarea { | ||||
|         width: 100%; | ||||
|       } | ||||
|  | ||||
|       span:first-child { | ||||
|         font-size: 28px; | ||||
|         color: #1365DD; | ||||
|       } | ||||
|  | ||||
|       span:last-child { | ||||
|         font-size: 24px; | ||||
|         color: #999999; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .u-btn { | ||||
|       width: 100%; | ||||
|       height: 112px !important; | ||||
|       font-size: 32px; | ||||
|       color: #FFFFFF; | ||||
|       position: fixed; | ||||
|       left: 0; | ||||
|       bottom: 0; | ||||
|       background: #197DF0 !important; | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
							
								
								
									
										432
									
								
								src/pages/documentFlow/components/detail.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,432 @@ | ||||
| <template> | ||||
|   <div class="detail"> | ||||
|     <template v-if="!userSelect"> | ||||
|       <div class="card"> | ||||
|         <header>{{detail.documentName}}</header> | ||||
|         <u-gap height="16"></u-gap> | ||||
|         <u-row> | ||||
|           <span>公文编号:</span> | ||||
|           <span>{{detail.documentCode}}</span> | ||||
|         </u-row> | ||||
|         <u-gap height="8"></u-gap> | ||||
|         <u-row> | ||||
|           <span>公文类型:</span> | ||||
|           <span>{{$dict.getLabel("officialDocumentName",detail.documentType)}}</span> | ||||
|         </u-row> | ||||
|         <u-gap height="8"></u-gap> | ||||
|         <u-row> | ||||
|           <span>紧急程度:</span> | ||||
|           <span :style="{color:$dict.getColor('documentEmergencyLevel',detail.emergencyLevel)}">{{$dict.getLabel("documentEmergencyLevel",detail.emergencyLevel)}}</span> | ||||
|         </u-row> | ||||
|         <u-gap height="8"></u-gap> | ||||
|         <u-row> | ||||
|           <span>发文机关:</span> | ||||
|           <span>{{detail.issuingUnit}}</span> | ||||
|         </u-row> | ||||
|         <u-gap height="8"></u-gap> | ||||
|         <u-row> | ||||
|           <span>发文字号:</span> | ||||
|           <span>{{detail.issuingFont}}</span> | ||||
|         </u-row> | ||||
|         <u-gap height="8"></u-gap> | ||||
|         <u-row> | ||||
|           <span>签发人:</span> | ||||
|           <span>{{detail.signer}}</span> | ||||
|         </u-row> | ||||
|         <u-gap height="16"></u-gap> | ||||
|         <img v-if="detail.confidentialityLevel" :src="$cdn + tag(detail.confidentialityLevel)" alt=""> | ||||
|       </div> | ||||
|       <div class="card" style="margin-bottom: 0;padding-top: 0"> | ||||
|         <div class="label">备注</div> | ||||
|         <span>{{detail.remark}}</span> | ||||
|       </div> | ||||
|       <div class="card" style="padding-top: 0" v-if="detail.files && detail.files.length"> | ||||
|         <div class="label">相关附件</div> | ||||
|         <div class="file" v-for="(item,index) in detail.files" :key="index" @click="preFile(item)"> | ||||
|           <u-row justify="between"> | ||||
|             <label class="left"> | ||||
|               <img :src="$cdn + 'common/appendix.png'" alt=""> | ||||
|               <span>{{item.fileName}}.{{item.postfix}}</span> | ||||
|             </label> | ||||
|             <span>{{(item.size/1024).toFixed(2)}}KB</span> | ||||
|           </u-row> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="card"> | ||||
|         <div class="label" style="96px;">{{detail.readType ==0 ? "流转信息" : "传阅情况"}} | ||||
|           <em>({{$dict.getLabel("documentStatus",detail.status)}})</em></div> | ||||
|         <div class="progress"> | ||||
|           <div class="item" v-for="(item,index) in detail.flowUsers" :key="index"> | ||||
|             <div class="avatar">{{item.flowUserName && item.flowUserName.substr(-2)}}</div> | ||||
|             <div class="right"> | ||||
|               <u-row justify="between"> | ||||
|                 <text class="status" :style="{color:item.readStatus==1?'#FF8822':'#1365DD'}">{{$dict.getLabel(detail.readType ==1 ? "readingStatus" : | ||||
|                   "documentFlowStatus",detail.readType ==0 ? item.flowStatus : item.readStatus)}} | ||||
|                 </text> | ||||
|                 <text class="date">{{item.flowTime}}</text> | ||||
|               </u-row> | ||||
|               <u-row justify="between"> | ||||
|                 <text class="name">{{item.flowUserName}}</text> | ||||
|               </u-row> | ||||
|               <u-row justify="between"> | ||||
|                 <text class="note">{{item.description}}</text> | ||||
|               </u-row> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <div class="footer" v-if="detail.flowRight==1 && detail.readType==0"> | ||||
|         <div @click="handleClick(0)">批示</div> | ||||
|         <div @click="handleClick(1)">流转</div> | ||||
|       </div> | ||||
|  | ||||
|       <div class="footer" v-if="detail.readType==1 && detail.flowRight==1" @click="read" style="background-color: #1365DD;color: #FFFFFF">我已阅完</div> | ||||
|  | ||||
|     </template> | ||||
|  | ||||
|     <AiSelectEnterprise :visible.sync="userSelect" v-if="userSelect" :multiple="false" | ||||
|                         @change="change"></AiSelectEnterprise> | ||||
|  | ||||
|     <AiBack v-if="!userSelect"/> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import AiBack from "../../../components/AiBack"; | ||||
|   import AiSelectEnterprise from "../../../components/AiSelectEnterprise/AiSelectEnterprise"; | ||||
|   import {mapActions} from "vuex"; | ||||
|  | ||||
|   export default { | ||||
|     name: "detail", | ||||
|     components: {AiBack, AiSelectEnterprise}, | ||||
|     data() { | ||||
|       return { | ||||
|         id: null, | ||||
|         detail: {}, | ||||
|         userSelect: false, | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     onLoad(opt) { | ||||
|       this.$dict.load("officialDocumentName", "documentEmergencyLevel", "documentStatus", "readingStatus", "documentFlowStatus") | ||||
|       this.id = opt.id | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       ...mapActions(['previewFile', 'injectJWeixin']), | ||||
|       read() { | ||||
|         this.$http.post("/app/appofficialdocumentinfo/readById", null, { | ||||
|           params: { | ||||
|             id: this.id | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.$u.toast("已阅读") | ||||
|             this.getDetail() | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|       preFile(e) { | ||||
|         if([".jpg",".png",".gif"].includes(e.postfix.toLowerCase())){ | ||||
|           uni.previewImage({ | ||||
|             current: e.url, | ||||
|             urls: [e.url] | ||||
|           }) | ||||
|         }else { | ||||
|           this.previewFile({...e}) | ||||
|         } | ||||
|       }, | ||||
|       change(e) { | ||||
|         this.$http.post("/app/appofficialdocumentinfo/flowById", null, { | ||||
|           params: { | ||||
|             flowUserId: e[0].id, | ||||
|             flowUserName: e[0].name, | ||||
|             id: this.id, | ||||
|             avatar: e[0].avatar, | ||||
|             flag: 0 | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.$u.toast("流转成功") | ||||
|             this.getDetail() | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       tag(status) { | ||||
|         return { | ||||
|           "0": "common/mm.png", | ||||
|           "1": "common/jm.png", | ||||
|           "2": "common/tm.png" | ||||
|         }[status] | ||||
|       }, | ||||
|       getDetail() { | ||||
|         this.$http.post("/app/appofficialdocumentinfo/queryDetailById", null, { | ||||
|           params: { | ||||
|             id: this.id, | ||||
|             flag: 1 | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res && res.data) { | ||||
|             this.detail = res.data | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|       handleClick(status) { | ||||
|         if (status == 0) { | ||||
|           uni.navigateTo({ | ||||
|             url: "/pages/documentFlow/components/approval?id=" + this.id | ||||
|           }) | ||||
|         } else { | ||||
|           this.userSelect = true | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     onShow() { | ||||
|       this.getDetail() | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .detail { | ||||
|     min-height: 100%; | ||||
|     background-color: #F5F5F5; | ||||
|     padding-bottom: 140px; | ||||
|     position: relative; | ||||
|  | ||||
|     .card { | ||||
|       background-color: #FFFFFF; | ||||
|       margin-bottom: 8px; | ||||
|       box-sizing: border-box; | ||||
|       padding: 16px 32px; | ||||
|       position: relative; | ||||
|  | ||||
|       header { | ||||
|         font-size: 40px; | ||||
|         font-weight: 600; | ||||
|         color: #333333; | ||||
|         line-height: 64px; | ||||
|         letter-spacing: 1px; | ||||
|       } | ||||
|  | ||||
|       .u-row { | ||||
|         & > div { | ||||
|           background-color: #2266FF; | ||||
|           border-radius: 50%; | ||||
|           text-align: center; | ||||
|           font-size: 22px; | ||||
|           font-weight: bold; | ||||
|           color: #FFFFFF; | ||||
|           display: flex; | ||||
|           align-items: center; | ||||
|           justify-content: center; | ||||
|         } | ||||
|  | ||||
|         & > span:first-child { | ||||
|           font-size: 30px; | ||||
|           color: #999999;; | ||||
|           line-height: 48px; | ||||
|         } | ||||
|  | ||||
|         & > span:last-child { | ||||
|           font-size: 30px; | ||||
|           color: #343D65; | ||||
|           margin-left: 16px; | ||||
|           line-height: 48px; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       & > img { | ||||
|         width: 190px; | ||||
|         height: 190px; | ||||
|         position: absolute; | ||||
|         right: 0; | ||||
|         top: 74px; | ||||
|       } | ||||
|  | ||||
|       & > span { | ||||
|         font-size: 32px; | ||||
|         color: #333333; | ||||
|         line-height: 48px; | ||||
|         letter-spacing: 1px; | ||||
|         display: inline-block; | ||||
|       } | ||||
|  | ||||
|       .label { | ||||
|         height: 80px; | ||||
|         font-size: 32px; | ||||
|         color: #333333; | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         margin-bottom: 16px; | ||||
|  | ||||
|         & > em { | ||||
|           font-style: normal; | ||||
|           font-size: 32px; | ||||
|           color: #1365DD; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .file { | ||||
|         height: 128px; | ||||
|         background: #FFFFFF; | ||||
|         border-radius: 8px; | ||||
|         border: 1px solid #CCCCCC; | ||||
|         box-sizing: border-box; | ||||
|         padding: 0 16px; | ||||
|         margin-bottom: 32px; | ||||
|  | ||||
|         & > .u-row { | ||||
|           height: 100%; | ||||
|  | ||||
|           .left { | ||||
|             width: 500px; | ||||
|             display: flex; | ||||
|             align-items: center; | ||||
|  | ||||
|             & > img { | ||||
|               flex-shrink: 0; | ||||
|               width: 96px; | ||||
|               height: 96px; | ||||
|             } | ||||
|  | ||||
|             & > span { | ||||
|               font-size: 32px; | ||||
|               color: #333333; | ||||
|               display: inline-block; | ||||
|               line-height: 44px; | ||||
|               overflow: hidden; | ||||
|               text-overflow: ellipsis; | ||||
|               display:-webkit-box; | ||||
|               -webkit-box-orient:vertical; | ||||
|               -webkit-line-clamp:2; | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           & > span { | ||||
|             font-size: 28px; | ||||
|             color: #999999; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .active { | ||||
|         background-color: #F3F6F9; | ||||
|       } | ||||
|  | ||||
|       .progress { | ||||
|         margin-top: 8px; | ||||
|  | ||||
|         .item { | ||||
|           display: flex; | ||||
|           align-items: center; | ||||
|           position: relative; | ||||
|           min-height: 136px; | ||||
|           margin-bottom: 80px; | ||||
|  | ||||
|           .avatar { | ||||
|             width: 80px; | ||||
|             height: 80px; | ||||
|             border-radius: 50%; | ||||
|             background-color: #2266FF; | ||||
|             font-size: 28px; | ||||
|             color: #FFFFFF; | ||||
|             display: flex; | ||||
|             align-items: center; | ||||
|             justify-content: center; | ||||
|             flex-shrink: 0; | ||||
|           } | ||||
|  | ||||
|           .right { | ||||
|             width: 100%; | ||||
|             display: flex; | ||||
|             flex-direction: column; | ||||
|  | ||||
|             & > .u-row { | ||||
|               margin-left: 40px; | ||||
|  | ||||
|               .status { | ||||
|                 font-size: 32px; | ||||
|                 color: #333333; | ||||
|               } | ||||
|  | ||||
|               .date { | ||||
|                 font-size: 28px; | ||||
|                 color: #999999; | ||||
|               } | ||||
|  | ||||
|               .name { | ||||
|                 font-size: 28px; | ||||
|                 color: #666666; | ||||
|                 margin: 8px 0; | ||||
|               } | ||||
|  | ||||
|               .note { | ||||
|                 font-size: 28px; | ||||
|                 color: #343D65; | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           &:after { | ||||
|             content: ""; | ||||
|             width: 4px; | ||||
|             height: 100%; | ||||
|             background-color: #EEEEEE; | ||||
|             position: absolute; | ||||
|             left: 40px; | ||||
|             top: 112px; | ||||
|           } | ||||
|  | ||||
|           &:last-child:after { | ||||
|             display: none; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .footer { | ||||
|       height: 112px; | ||||
|       width: 100%; | ||||
|       position: fixed; | ||||
|       left: 0; | ||||
|       bottom: 0; | ||||
|       background-color: #FFFFFF; | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       justify-content: center; | ||||
|       font-size: 36px; | ||||
|  | ||||
|       & > div { | ||||
|         color: #333333; | ||||
|       } | ||||
|  | ||||
|       & > div:first-child { | ||||
|         width: 50%; | ||||
|         height: 100%; | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|       } | ||||
|  | ||||
|       & > div:last-child { | ||||
|         width: 50%; | ||||
|         height: 100%; | ||||
|         color: #FFFFFF; | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         background-color: #1365DD; | ||||
|       } | ||||
|  | ||||
|       & > label { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         background-color: #1365DD; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
							
								
								
									
										166
									
								
								src/pages/documentFlow/documentFlow.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,166 @@ | ||||
| <template> | ||||
|   <div class="document-flow"> | ||||
|     <ai-top-fixed> | ||||
|       <header class="pad"> | ||||
|         <u-search placeholder="请输入公文名称" v-model="documentName" @clear="documentName='',getList()" @search="getList" clearabled :show-action="false" height="64"></u-search> | ||||
|       </header> | ||||
|     </ai-top-fixed> | ||||
|     <div class="list pad" v-if="list.length"> | ||||
|       <div class="card" v-for="(item,index) in list" :key="index" @click="handleClick(item)"> | ||||
|         <u-row> | ||||
|           <em v-if="item.redStatus==0"></em> | ||||
|           <span>{{item.documentName}}</span> | ||||
|         </u-row> | ||||
|         <u-gap height="16"></u-gap> | ||||
|         <u-row> | ||||
|           <label>公文类型:</label> | ||||
|           <text style="color: #1365DD;">{{$dict.getLabel("officialDocumentName",item.documentType)}}</text> | ||||
|         </u-row> | ||||
|         <u-gap height="8"></u-gap> | ||||
|         <u-row> | ||||
|           <label>登记人:</label> | ||||
|           <text>{{item.createUserName}}</text> | ||||
|         </u-row> | ||||
|         <u-gap height="8"></u-gap> | ||||
|         <u-row> | ||||
|           <label>登记日期:</label> | ||||
|           <text>{{item.createTime}}</text> | ||||
|         </u-row> | ||||
|         <img :src=" $cdn + tag(item.readType)" alt=""> | ||||
|       </div> | ||||
|     </div> | ||||
|     <AiEmpty v-else></AiEmpty> | ||||
|     <u-loadmore :status="status" v-if="list.length"/> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import AiTopFixed from "../../components/AiTopFixed"; | ||||
|   import AiEmpty from "../../components/AiEmpty/AiEmpty"; | ||||
|  | ||||
|   export default { | ||||
|     name: "documentFlow", | ||||
|     components: {AiTopFixed,AiEmpty}, | ||||
|     data() { | ||||
|       return { | ||||
|         documentName: "", | ||||
|         current: 1, | ||||
|         list: [], | ||||
|         status: "加载更多" | ||||
|       } | ||||
|     }, | ||||
|     onLoad(){ | ||||
|       this.$dict.load("officialDocumentName") | ||||
|     }, | ||||
|     methods: { | ||||
|       tag(status){ | ||||
|         return { | ||||
|           "0": 'common/1ps.png', | ||||
|           "1": 'common/2cy.png' | ||||
|         }[status] | ||||
|       }, | ||||
|       getList() { | ||||
|         this.$http.post("/app/appofficialdocumentinfo/appList", null, { | ||||
|           params:{ | ||||
|             documentName: this.documentName, | ||||
|             size: 10, | ||||
|             current: this.current | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res && res.data) { | ||||
|             if (this.current > 1 && this.current > res.data.pages) { | ||||
|               this.status = "已经到底啦" | ||||
|             } | ||||
|             this.list = this.current > 1 ? [...this.list, ...res.data.records] : res.data.records | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|       handleClick({id}) { | ||||
|         uni.navigateTo({ | ||||
|           url: "/pages/documentFlow/components/detail?id=" + id | ||||
|         }) | ||||
|       } | ||||
|     }, | ||||
|     onShow(){ | ||||
|       this.getList() | ||||
|     }, | ||||
|     onReachBottom() { | ||||
|       this.current = this.current + 1; | ||||
|       this.getList() | ||||
|     }, | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .document-flow { | ||||
|     min-height: 100%; | ||||
|     background: #F5F5F5; | ||||
|  | ||||
|     ::v-deep .content { | ||||
|       padding: 0; | ||||
|     } | ||||
|  | ||||
|     header { | ||||
|       height: 112px; | ||||
|       background-color: #FFFFFF; | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|     } | ||||
|  | ||||
|     .list { | ||||
|       margin: 32px 0; | ||||
|  | ||||
|       .card { | ||||
|         background: #FFFFFF; | ||||
|         border-radius: 8px; | ||||
|         box-sizing: border-box; | ||||
|         padding: 32px; | ||||
|         position: relative; | ||||
|         margin-bottom: 32px; | ||||
|  | ||||
|         .u-row{ | ||||
|           flex-wrap: nowrap !important; | ||||
|         } | ||||
|  | ||||
|         em { | ||||
|           width: 16px; | ||||
|           height: 16px; | ||||
|           border-radius: 50%; | ||||
|           background-color: #FF4466; | ||||
|           font-style: normal; | ||||
|           margin-right: 8px; | ||||
|           flex-shrink: 0; | ||||
|         } | ||||
|  | ||||
|         span { | ||||
|           font-size: 32px; | ||||
|           font-weight: 600; | ||||
|           color: #333333; | ||||
|         } | ||||
|  | ||||
|         label { | ||||
|           font-size: 30px; | ||||
|           color: #999999; | ||||
|         } | ||||
|  | ||||
|         text { | ||||
|           font-size: 30px; | ||||
|           color: #343D65; | ||||
|         } | ||||
|  | ||||
|         img { | ||||
|           width: 160px; | ||||
|           height: 160px; | ||||
|           position: absolute; | ||||
|           right: 0; | ||||
|           bottom: 0; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .pad { | ||||
|       box-sizing: border-box; | ||||
|       padding: 32px 32px 0 32px; | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
							
								
								
									
										146
									
								
								src/pages/guardianship/component/areaSelector.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,146 @@ | ||||
| <template> | ||||
|   <section class="areaSelector"> | ||||
|     <ai-search-popup mode="bottom" ref="areaSelector"> | ||||
|       <template #btn> | ||||
|         <div class="areaSelector"> | ||||
|           <span v-for="area in fullArea" :key="area.id" v-text="area.name" | ||||
|                 :class="{current:area.id==areaId}"  @tap="index=area.id,getChildAreas(area.id)"/> | ||||
|         </div> | ||||
|       </template> | ||||
|       <div class="areaSelector"> | ||||
|         <span v-for="area in fullArea" :key="area.id" v-text="area.name" | ||||
|               :class="{current:area.id==index}" | ||||
|               @click="index=area.id,getChildAreas(area.id)"/> | ||||
|       </div> | ||||
|       <div class="pendingItem" flex v-for="op in list" :key="op.id" @tap="handleSelect(op)"> | ||||
|         <div class="fill" :class="{self:index==op.id}" v-html="op.name"/> | ||||
|         <u-icon name="arrow-right" color="#ddd"/> | ||||
|       </div> | ||||
|     </ai-search-popup> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import AiSearchPopup from "../../../components/AiSearchPopup"; | ||||
| import AiCell from "../../../components/AiCell"; | ||||
| import {mapState} from "vuex"; | ||||
|  | ||||
| export default { | ||||
|   name: "areaSelector", | ||||
|   components: {AiCell, AiSearchPopup}, | ||||
|   props: { | ||||
|     areaId: {default: ""} | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|     dataRange() { | ||||
|       let rules = [10, 8, 6, 3, 0], level = 2 | ||||
|       rules.some((e, i) => { | ||||
|         let reg = new RegExp(`0{${e}}`, 'g') | ||||
|         if (reg.test(this.user.areaId)) { | ||||
|           return level = i | ||||
|         } | ||||
|       }) | ||||
|       return level | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       fullArea: [], | ||||
|       index: "", | ||||
|       list: [] | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     areaId(v) { | ||||
|       v && this.getFullArea() | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getFullArea() { | ||||
|       let {areaId} = this | ||||
|       return areaId && this.$http.post("/admin/area/getAllParentAreaId", null, { | ||||
|         params: {areaId} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.fullArea = res.data.reverse().slice(this.dataRange) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     getChildAreas(id) { | ||||
|       id && this.$http.post("/admin/area/queryAreaByParentId", null, { | ||||
|         params: {id} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.list = res.data | ||||
|           let self = this.fullArea.find(e => e.id == this.index) | ||||
|           this.list.unshift(self) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleSelect(op) { | ||||
|       this.$emit('select', op) | ||||
|       this.$refs.areaSelector?.handleSelect() | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.index = this.areaId | ||||
|     this.getFullArea() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .areaSelector { | ||||
|   ::v-deep .AiSearchPopup { | ||||
|     .areaSelector { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|  | ||||
|       span { | ||||
|         cursor: pointer; | ||||
|  | ||||
|         &:first-of-type:before { | ||||
|           content: ""; | ||||
|           padding: 0; | ||||
|         } | ||||
|  | ||||
|         &:before { | ||||
|           color: #333; | ||||
|           content: "/"; | ||||
|           padding: 0 16px; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .current { | ||||
|         color: #3F8DF5; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .u-drawer-content { | ||||
|       position: fixed; | ||||
|  | ||||
|       .areaSelector { | ||||
|         padding: 0 16px; | ||||
|         box-sizing: border-box; | ||||
|         border-bottom: 16px solid #f5f5f5; | ||||
|  | ||||
|         span { | ||||
|           line-height: 100px; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .pendingItem { | ||||
|       margin-left: 32px; | ||||
|       padding-right: 32px; | ||||
|       height: 104px; | ||||
|       border-bottom: 1px solid #ddd; | ||||
|  | ||||
|       .self { | ||||
|         font-weight: bold; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										73
									
								
								src/pages/guardianship/component/makeCalls.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,73 @@ | ||||
| <template> | ||||
|   <section class="makeCalls"> | ||||
|     <div v-if="$slots.default" @tap="calls=true"> | ||||
|       <slot/> | ||||
|     </div> | ||||
|     <div v-else flex class="column" @tap="calls=true"> | ||||
|       <img :src="`${$cdn}guardianship/dh.png`"/> | ||||
|       <span v-html="label"/> | ||||
|     </div> | ||||
|     <u-popup v-model="calls" mode="bottom"> | ||||
|       <div flex class="column option" v-for="item in list" @click="handleCall(item.guardianPhone)"> | ||||
|         {{ [item.guardianName, item.guardianPhone].join('   ') }} | ||||
|       </div> | ||||
|       <div class="option" @tap="calls=false">取消</div> | ||||
|     </u-popup> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "makeCalls", | ||||
|   props: { | ||||
|     label: {default: "联系ta"}, | ||||
|     list: {default: () => []} | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       calls: false | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     handleCall(phone) { | ||||
|       location.href = "tel:" + phone | ||||
|     }, | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .makeCalls { | ||||
|   img { | ||||
|     width: 40px; | ||||
|     height: 40px; | ||||
|   } | ||||
|  | ||||
|   ::v-deep span { | ||||
|     margin-left: 0; | ||||
|     color: #999; | ||||
|     font-size: 20px; | ||||
|  | ||||
|     p { | ||||
|       color: #3D94FB; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ::v-deep .u-drawer { | ||||
|     text-align: center; | ||||
|     .uni-scroll-view-content{ | ||||
|       max-height: 672px; | ||||
|     } | ||||
|     .option { | ||||
|       font-size: 32px; | ||||
|       cursor: pointer; | ||||
|       line-height: 112px; | ||||
|       border-bottom: 1px solid #D8DDE6; | ||||
|  | ||||
|       &:first-of-type { | ||||
|         border-radius: 16px 16px 0 0; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										40
									
								
								src/pages/guardianship/component/openMap.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,40 @@ | ||||
| <template> | ||||
|   <section class="openMap"> | ||||
|     <div flex class="column" shrink @tap="handleOpenMap"> | ||||
|       <img :src="`${$cdn}guardianship/seat.png`"/> | ||||
|       <span v-text="'地图/导航'"/> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "openMap", | ||||
|   props: { | ||||
|     data: {default: () => ({})} | ||||
|   }, | ||||
|   methods: { | ||||
|     handleOpenMap() { | ||||
|       let {lng, lat, gpsDesc} = this.data | ||||
|       location.href = `https://uri.amap.com/marker?callnative=1&position=${[lng, lat].toString()}&name=${gpsDesc}` | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .openMap { | ||||
|   flex-shrink: 0; | ||||
|  | ||||
|   img { | ||||
|     width: 40px; | ||||
|     height: 40px; | ||||
|   } | ||||
|  | ||||
|   span { | ||||
|     margin-left: 0; | ||||
|     color: #999; | ||||
|     font-size: 20px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										169
									
								
								src/pages/guardianship/earlyWarning.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,169 @@ | ||||
| <template> | ||||
|   <section class="earlyWarning"> | ||||
|     <ai-top-fixed> | ||||
|       <u-search v-model="search.name" placeholder="请输入姓名" :show-action="false" | ||||
|                 search-icon-color="#ccc" placeholder-color="#999" | ||||
|                 @change="page.current=1,getList()"/> | ||||
|       <div flex> | ||||
|         <ai-date class="fill" placeholder="日期选择" mode="range" @change="handleDateSearch"/> | ||||
|         <ai-select class="fill" dict="intelligentGuardianshipItem2" @data="handleTypeSearch"> | ||||
|           <div>{{ $dict.getLabel('intelligentGuardianshipItem2', search.item) || '全部预警' }}</div> | ||||
|           <i class="iconfont iconfont-iconArrow_Down"/> | ||||
|         </ai-select> | ||||
|       </div> | ||||
|     </ai-top-fixed> | ||||
|     <div class="card" v-for="row in list" :key="row.id" @tap="handleShow(row)"> | ||||
|       <div class="header" flex> | ||||
|         <img :src="top.cdn(typeIcons[row.item])"/> | ||||
|         <b v-text="row.desc"/> | ||||
|       </div> | ||||
|       <div class="wrapper"> | ||||
|         <div class="start" flex> | ||||
|           <span v-text="`上报时间:`"/> | ||||
|           <div v-text="row.createTime"/> | ||||
|         </div> | ||||
|         <div class="start" flex> | ||||
|           <span v-text="`上报地点:`"/> | ||||
|           <div v-text="row.gpsDesc"/> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import AiTopFixed from "../../components/AiTopFixed"; | ||||
| import {mapState} from "vuex"; | ||||
| import AiDate from "../../components/AiDate"; | ||||
| import AiSelect from "../../components/AiSelect"; | ||||
|  | ||||
| export default { | ||||
|   name: "earlyWarning", | ||||
|   components: {AiSelect, AiDate, AiTopFixed}, | ||||
|   inject: ['top'], | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|     typeIcons() { | ||||
|       return { | ||||
|         0: "icon4", | ||||
|         1: "icon2", | ||||
|         2: "icon5", | ||||
|         3: "icon6", | ||||
|         4: "icon3", | ||||
|         5: "icon1", | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       search: {name: "", createTimeRange: ",", item: ""}, | ||||
|       areaId: "", | ||||
|       page: {current: 1, size: 10, total: 0}, | ||||
|       list: [] | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getList() { | ||||
|       let {areaId} = this | ||||
|       this.$http.post("/app/appintelligentguardianshipalarm/list", null, { | ||||
|         params: {areaId, ...this.search, ...this.page} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           let data = res.data.records.map(e => { | ||||
|             return {...e, desc: [e.name, this.$dict.getLabel('intelligentGuardianshipItem2', e.item)].join('的')} | ||||
|           }) | ||||
|           if (this.page.current > 1) { | ||||
|             this.list = [...this.list, ...data] | ||||
|           } else this.list = data | ||||
|           this.page.total = res.data.total | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     reachBottom() { | ||||
|       if (this.page.total > this.list.length) { | ||||
|         this.page.current++ | ||||
|         this.getList() | ||||
|       } | ||||
|     }, | ||||
|     handleDateSearch(v) { | ||||
|       let {startDate: start, endDate: end} = v | ||||
|       start = this.$dateFormat(start) | ||||
|       end = this.$dateFormat(end) | ||||
|       this.search.createTimeRange = [start, end || start].toString() | ||||
|       this.page.current = 1 | ||||
|       this.getList() | ||||
|     }, | ||||
|     handleTypeSearch(v) { | ||||
|       this.search.item = v?.[0]?.value | ||||
|       this.page.current = 1 | ||||
|       this.getList() | ||||
|     }, | ||||
|     handleShow(row) { | ||||
|       uni.navigateTo({url: `./warningDetail?id=${row.id}`}) | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.areaId = JSON.parse(JSON.stringify(this.user.areaId)) | ||||
|     this.getList() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .earlyWarning { | ||||
|   padding-bottom: 130px; | ||||
|   background: #f5f5f5; | ||||
|  | ||||
|   ::v-deep .AiDate > div { | ||||
|     justify-content: center; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .u-drawer-content { | ||||
|     padding-bottom: 100px; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .display { | ||||
|     justify-content: center; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .iconfont-iconArrow_Down { | ||||
|     margin-left: 4px; | ||||
|     font-size: 32px; | ||||
|     color: inherit; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .card { | ||||
|     margin: 32px 32px 0; | ||||
|     background: #FFFFFF; | ||||
|     box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.02); | ||||
|     border-radius: 8px; | ||||
|  | ||||
|     .header { | ||||
|       padding: 0 32px; | ||||
|       height: 104px; | ||||
|       border-bottom: 2px solid #EFEFF4; | ||||
|       font-size: 36px; | ||||
|  | ||||
|       img { | ||||
|         width: 64px; | ||||
|         height: 64px; | ||||
|         margin-right: 16px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .wrapper { | ||||
|       color: #343D65; | ||||
|       font-size: 30px; | ||||
|       margin-bottom: 8px; | ||||
|       padding: 18px 32px; | ||||
|  | ||||
|       span { | ||||
|         white-space: nowrap; | ||||
|         flex-shrink: 0; | ||||
|         color: #999; | ||||
|         margin-right: 20px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										461
									
								
								src/pages/guardianship/gsLocation.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,461 @@ | ||||
| <template> | ||||
|   <section class="gsLocation"> | ||||
|     <ai-map class="fill" :map.sync="amap" :lib.sync="mapLib"/> | ||||
|     <div class="searchZone"> | ||||
|       <u-search v-model="search" placeholder="请输入姓名" shape="square" bg-color="#fff" | ||||
|                 :show-action="false" @search="getList()"/> | ||||
|       <div class="searchResult" v-if="searchResult"> | ||||
|         <div class="item" v-for="row in list" :key="row.id" @tap="handleSelect(row)"> | ||||
|           <img :src="cdn(row.onlineStatus==1?'zxtx':'lxtx')"/> | ||||
|           <div flex class="column fill"> | ||||
|             <b v-html="searchName(row.name)"/> | ||||
|             <div v-text="row.gpsDesc"/> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|       <u-popup v-model="popup" mode="bottom" :mask="false"> | ||||
|         <div class="headerIcon" flex @touchstart="handleTouchStart" @touchmove="handleTouchmoveClose"/> | ||||
|         <div class="selectedInfo"> | ||||
|           <div class="header" flex> | ||||
|             <img :src="`${$cdn}guardianship/tx.png`" @tap="handleShowDetail(selected)"/> | ||||
|             <b v-text="selected.name" @tap="handleShowDetail(selected)"/> | ||||
|             <div v-if="selected.abnormalStatus==1" class="abnormal" @tap="handleShowDetail(selected)">异常</div> | ||||
|             <u-icon name="arrow-right" color="#ddd" class="fill" @tap="handleShowDetail(selected)"/> | ||||
|             <make-calls :list="phoneList"/> | ||||
|           </div> | ||||
|           <div flex class="spb wrap"> | ||||
|             <div class="detail" v-for="(op,i) in quotas" :key="i" flex> | ||||
|               <img :src="op.icon"/> | ||||
|               <div class="fill" v-text="op.label"/> | ||||
|               <div :class="{abnormal:op.abnormal}" v-text="op.value"/> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="navigation"> | ||||
|           <div class="content" flex> | ||||
|             <div flex class="spb wrap"> | ||||
|               <div class="fill" v-text="selected.gpsDesc"/> | ||||
|               <span>最后更新:{{ selected.lastUpdateTime }}</span> | ||||
|               <div class="battery" flex> | ||||
|                 <img :src="batteryIcon"/> | ||||
|                 <div v-text="`剩余${selected.electricQuantity}%`"/> | ||||
|               </div> | ||||
|             </div> | ||||
|             <open-map :data="selected"/> | ||||
|           </div> | ||||
|         </div> | ||||
|       </u-popup> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import AiSearchPopup from "../../components/AiSearchPopup"; | ||||
| import {mapState} from "vuex"; | ||||
| import UPopup from "../../uview/components/u-popup/u-popup"; | ||||
| import MakeCalls from "./component/makeCalls"; | ||||
| import OpenMap from "./component/openMap"; | ||||
| import AiMap from "../../components/AiMap"; | ||||
|  | ||||
| export default { | ||||
|   name: "gsLocation", | ||||
|   components: {AiMap, OpenMap, MakeCalls, UPopup, AiSearchPopup}, | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|     markers() { | ||||
|       return this.list.filter(e => e.lng).map(e => { | ||||
|         let abnormal = 'offline' | ||||
|         if (e.onlineStatus == 1) { | ||||
|           switch (e.abnormalStatus) { | ||||
|             case '1': | ||||
|               abnormal = 'warning'; | ||||
|               break; | ||||
|             case '2': | ||||
|               abnormal = 'abnormal'; | ||||
|               break; | ||||
|             default: | ||||
|               abnormal = '' | ||||
|           } | ||||
|         } | ||||
|         return new this.mapLib.Marker({ | ||||
|           position: new this.mapLib.LngLat(e.lng, e.lat), | ||||
|           anchor: 'bottom-center', | ||||
|           content: `<div class="marker ${abnormal}">${e.name}</div>`, | ||||
|           extData: e, | ||||
|           topWhenClick: true | ||||
|         }).on('click', () => { | ||||
|           this.handleSelect(e) | ||||
|         }) | ||||
|       }) | ||||
|     }, | ||||
|     quotas() { | ||||
|       let quota = [ | ||||
|         {key: "0", icon: "1"}, | ||||
|         {key: "1", icon: "2"}, | ||||
|         {key: "2", icon: "3"}, | ||||
|         {key: "3", icon: "4"}, | ||||
|       ] | ||||
|       return quota.map(e => { | ||||
|         let item = this.detail.find(d => d.item == e.key) | ||||
|         let label = this.$dict.getLabel('intelligentGuardianshipItem', e.key) | ||||
|         return { | ||||
|           label, icon: this.cdn(e.icon), | ||||
|           value: item?.itemValue || "-", | ||||
|           abnormal: item?.abnormalStatus == 1 | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     batteryIcon() { | ||||
|       return this.cdn(this.selected.electricQuantity == 100 ? 'dcm' : 'dcq') | ||||
|     }, | ||||
|     phoneList() { | ||||
|       let {name: guardianName, phone: guardianPhone} = this.selected | ||||
|       return [{guardianName, guardianPhone}, ...(this.selected.guardians || [])] | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       mapLib: null, | ||||
|       amap: null, | ||||
|       search: "", | ||||
|       selected: {}, | ||||
|       list: [], | ||||
|       popup: false,//被监护人信息弹窗 | ||||
|       detail: [], | ||||
|       moveDistance: 0, | ||||
|       searchResult: false //搜索下拉弹窗 | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     amap(v) { | ||||
|       v && this.getList().then(() => { | ||||
|         this.amap?.add(this.markers) | ||||
|         this.getMapArea() | ||||
|       }) | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     cdn(icon) { | ||||
|       return `${this.$cdn}guardianship/${icon}.png` | ||||
|     }, | ||||
|     getMapArea() { | ||||
|       if (this.mapLib) { | ||||
|         new this.mapLib.DistrictSearch({ | ||||
|           subdistrict: 0,   //获取边界不需要返回下级行政区 | ||||
|           extensions: 'all',  //返回行政区边界坐标组等具体信息 | ||||
|           level: 'district'  //查询行政级别为 市 | ||||
|         }).search(this.user.areaId.substring(0, 6), (status, result) => { | ||||
|           let bounds = result?.districtList?.[0]?.boundaries; | ||||
|           let polygons = [] | ||||
|           bounds?.forEach(path => polygons.push(new this.mapLib.Polygon({ | ||||
|             strokeWeight: 1, | ||||
|             path, | ||||
|             strokeStyle: 'dashed', | ||||
|             fillOpacity: 0.1, | ||||
|             fillColor: '#80d8ff', | ||||
|             strokeColor: '#0091ea' | ||||
|           }))) | ||||
|           this.amap.add(polygons) | ||||
|           this.amap.setFitView();//视口自适应 | ||||
|         }) | ||||
|       } | ||||
|     }, | ||||
|     getList() { | ||||
|       this.searchResult = !!this.search | ||||
|       return this.$http.post("/app/appintelligentguardianshipdevice/list", null, { | ||||
|         params: {name: this.search, size: 999, areaId: this.user.areaId} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.list = res.data.records | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     getDetail(deviceId) { | ||||
|       this.$http.post("/app/appintelligentguardianshipdevice/queryMonitorList", null, { | ||||
|         params: {type: 1, deviceId} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.detail = res.data.records | ||||
|         } | ||||
|       }) | ||||
|       this.$http.post("/app/appintelligentguardianshipdevice/queryDetailById", null, { | ||||
|         params: {id: deviceId} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.selected = {...this.selected, ...res.data} | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     searchName(name) { | ||||
|       return name?.replace(this.search, `<span>${this.search}</span>`) | ||||
|     }, | ||||
|     handleSelect(e) { | ||||
|       this.amap.setCenter(new this.mapLib.LngLat(e.lng, e.lat)) | ||||
|       this.selected = e | ||||
|       this.popup = true | ||||
|       this.searchResult = false | ||||
|       this.getDetail(e.id) | ||||
|     }, | ||||
|     handleShowDetail(user) { | ||||
|       uni.navigateTo({url: `./userDetail?id=${user.id}`}) | ||||
|     }, | ||||
|     handleTouchmoveClose(e) { | ||||
|       if (e.touches?.[0]?.clientY > this.moveDistance + 10) { | ||||
|         this.popup = false | ||||
|       } | ||||
|     }, | ||||
|     handleTouchStart(e) { | ||||
|       this.moveDistance = e.touches?.[0]?.clientY | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .gsLocation { | ||||
|   height: 100%; | ||||
|  | ||||
|   a { | ||||
|     color: inherit; | ||||
|     text-decoration: none; | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|     height: 112px; | ||||
|     border-top: 1px solid #d8dde6; | ||||
|  | ||||
|     &:first-of-type { | ||||
|       border-top: none; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .headerIcon { | ||||
|     width: 100%; | ||||
|     height: 60px; | ||||
|     justify-content: center; | ||||
|  | ||||
|     &:before { | ||||
|       content: " "; | ||||
|       display: block; | ||||
|       width: 64px; | ||||
|       height: 10px; | ||||
|       background: #CCCCCC; | ||||
|       border-radius: 5px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ::v-deep .makeCalls { | ||||
|     .option:last-of-type { | ||||
|       margin-bottom: 120px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ::v-deep .selectedInfo { | ||||
|     width: 100%; | ||||
|     border-radius: 20px 20px 0 0; | ||||
|     padding: 0 32px; | ||||
|     background: #fff; | ||||
|     overflow: hidden; | ||||
|     box-sizing: border-box; | ||||
|  | ||||
|     .header { | ||||
|       margin-bottom: 40px; | ||||
|       font-size: 40px; | ||||
|  | ||||
|       & > img { | ||||
|         width: 82px; | ||||
|         height: 82px; | ||||
|         margin-right: 16px; | ||||
|       } | ||||
|  | ||||
|       .abnormal { | ||||
|         color: #FF4466; | ||||
|         font-size: 24px; | ||||
|         padding: 12px; | ||||
|         background: rgba(#EC4461, .1); | ||||
|         border-radius: 8px; | ||||
|         margin: 0 16px; | ||||
|       } | ||||
|  | ||||
|       span { | ||||
|         margin-left: 0; | ||||
|         color: #999; | ||||
|         font-size: 20px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .detail { | ||||
|       width: 318px; | ||||
|       height: 84px; | ||||
|       background: #F4F5F6; | ||||
|       border-radius: 8px; | ||||
|       padding: 0 24px; | ||||
|       box-sizing: border-box; | ||||
|       margin-bottom: 36px; | ||||
|  | ||||
|       img { | ||||
|         margin-right: 16px; | ||||
|         width: 56px; | ||||
|         height: 56px; | ||||
|       } | ||||
|  | ||||
|       .abnormal { | ||||
|         color: #FF4466; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .navigation { | ||||
|     padding-bottom: 100px; | ||||
|  | ||||
|     .content { | ||||
|       padding: 32px 40px; | ||||
|       font-size: 26px; | ||||
|  | ||||
|       & > .spb { | ||||
|         margin-right: 40px; | ||||
|       } | ||||
|  | ||||
|       .fill { | ||||
|         min-width: 100%; | ||||
|         flex-shrink: 0; | ||||
|         margin-bottom: 10px; | ||||
|       } | ||||
|  | ||||
|       span { | ||||
|         margin-left: 0; | ||||
|         color: #999; | ||||
|         font-size: 22px; | ||||
|       } | ||||
|  | ||||
|       .battery > img { | ||||
|         margin-right: 14px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     &:before { | ||||
|       content: " "; | ||||
|       display: block; | ||||
|       width: 100%; | ||||
|       height: 8px; | ||||
|       background: #F4F5F6; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ::v-deep .searchZone { | ||||
|     position: absolute; | ||||
|     top: 0; | ||||
|     z-index: 2; | ||||
|     width: 100%; | ||||
|     background: transparent; | ||||
|     padding: 24px 16px; | ||||
|     box-sizing: border-box; | ||||
|  | ||||
|     .u-search { | ||||
|       box-shadow: 0 4px 8px 0 rgba(192, 185, 185, 0.5); | ||||
|     } | ||||
|  | ||||
|     .searchResult { | ||||
|       margin-top: 16px; | ||||
|       padding: 0 28px; | ||||
|       background: #fff; | ||||
|  | ||||
|       .item { | ||||
|         font-size: 24px; | ||||
|         display: flex; | ||||
|         line-height: 36px; | ||||
|         padding: 24px 0; | ||||
|         border-bottom: 3px solid #DEDFE1; | ||||
|  | ||||
|         &:last-of-type { | ||||
|           border-bottom: none; | ||||
|         } | ||||
|  | ||||
|         img { | ||||
|           width: 36px; | ||||
|           height: 36px; | ||||
|           margin-right: 16px; | ||||
|         } | ||||
|  | ||||
|         & > .fill { | ||||
|           align-items: unset; | ||||
|  | ||||
|           b > span { | ||||
|             color: #1365DD; | ||||
|           } | ||||
|  | ||||
|           & > div { | ||||
|             width: 100%; | ||||
|             overflow: hidden; | ||||
|             text-overflow: ellipsis; | ||||
|             white-space: nowrap; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ::v-deep .marker { | ||||
|     color: #fff; | ||||
|     font-size: 30px; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
|     padding: 0 32px; | ||||
|     height: 56px; | ||||
|     white-space: nowrap; | ||||
|     background: #5088FF; | ||||
|     border-color: #5088FF; | ||||
|     border-radius: 52px; | ||||
|     position: relative; | ||||
|  | ||||
|  | ||||
|     &:after { | ||||
|       position: absolute; | ||||
|       display: block; | ||||
|       content: " "; | ||||
|       bottom: -12px; | ||||
|       left: 50%; | ||||
|       transform: translateX(-50%); | ||||
|       border: 12px solid transparent; | ||||
|       border-bottom: none; | ||||
|       height: 0; | ||||
|       width: 0; | ||||
|       border-top-color: inherit; | ||||
|     } | ||||
|  | ||||
|     &.offline { | ||||
|       background: #C4CAD4; | ||||
|       border-color: #C4CAD4; | ||||
|     } | ||||
|  | ||||
|     &.warning { | ||||
|       background: #FFAA44; | ||||
|       border-color: #FFAA44; | ||||
|     } | ||||
|  | ||||
|     &.abnormal { | ||||
|       background: #F46159; | ||||
|       border-color: #F46159; | ||||
|  | ||||
|       &:before { | ||||
|         position: absolute; | ||||
|         z-index: -1; | ||||
|         bottom: -40px; | ||||
|         width: 80px; | ||||
|         height: 80px; | ||||
|         border-radius: 50%; | ||||
|         background-color: #F46159; | ||||
|         transform: translate(-50%, -50%); | ||||
|         animation: mapWarn 1s ease-out 0s infinite; | ||||
|         content: " "; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .AiMap { | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										65
									
								
								src/pages/guardianship/guardianship.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,65 @@ | ||||
| <template> | ||||
|   <section class="guardianship"> | ||||
|     <component ref="currentTab" :is="currentTab.comp"/> | ||||
|     <ai-tabbar :active.sync="active" :list="bottomBar"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import AiLoading from "../../components/AiLoading"; | ||||
| import GsLocation from "./gsLocation"; | ||||
| import AiTabbar from "../../components/AiTabbar"; | ||||
| import WardList from "./wardList"; | ||||
| import EarlyWarning from "./earlyWarning"; | ||||
|  | ||||
| export default { | ||||
|   name: "guardianship", | ||||
|   components: {AiTabbar, AiLoading}, | ||||
|   provide() { | ||||
|     return { | ||||
|       top: this | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     bottomBar() { | ||||
|       return [ | ||||
|         {text: "定位", iconPath: "bardwn", selectedIconPath: "bardwh", comp: GsLocation}, | ||||
|         {text: "人员", iconPath: "barryn", selectedIconPath: "barryh", comp: WardList}, | ||||
|         {text: "预警", iconPath: "baryjn", selectedIconPath: "baryjh", comp: EarlyWarning}, | ||||
|       ].map(e => ({ | ||||
|         ...e, | ||||
|         iconPath: this.cdn(e.iconPath), | ||||
|         selectedIconPath: this.cdn(e.selectedIconPath) | ||||
|       })) | ||||
|     }, | ||||
|     currentTab() { | ||||
|       return this.bottomBar?.[this.active] || {} | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     cdn(icon) { | ||||
|       return `${this.$cdn}guardianship/${icon}.png` | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       active: 0 | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.$dict.load("intelligentGuardianshipItem", 'intelligentGuardianshipItem2', 'intelligentGuardianshipAbnormalStatus') | ||||
|   }, | ||||
|   onReachBottom() { | ||||
|     if (typeof this.$refs?.currentTab?.reachBottom == 'function') this.$refs?.currentTab.reachBottom() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .guardianship { | ||||
|   position: absolute; | ||||
|   width: 100%; | ||||
|   top: 0; | ||||
|   bottom: 0; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										75
									
								
								src/pages/guardianship/historyList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,75 @@ | ||||
| <template> | ||||
|   <section class="historyList"> | ||||
|     <div flex> | ||||
|       <b class="header" v-text="itemLabel"/> | ||||
|     </div> | ||||
|     <div v-for="row in list" :key="row.id" flex class="spb row"> | ||||
|       <div :class="{abnormal:row.abnormalStatus==1}" v-text="row.itemValue"/> | ||||
|       <span v-text="row.sampleTime"/> | ||||
|     </div> | ||||
|     <ai-back/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import AiBack from "../../components/AiBack"; | ||||
| export default { | ||||
|   name: "historyList", | ||||
|   components: {AiBack}, | ||||
|   computed: { | ||||
|     itemLabel() { | ||||
|       return '历史'+this.$dict.getLabel('intelligentGuardianshipItem', this.$route.query.type)+`(${this.$dict.getLabel('intelligentGuardianshipItemUnit',this.$route.query.type)})` | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       list: [] | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getHistory() { | ||||
|       let {type: item, id: deviceId} = this.$route.query | ||||
|       this.$http.post("/app/appintelligentguardianshipdevice/queryMonitorList", null, { | ||||
|         params: {deviceId, size: 999, item} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.list = res.data.records | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.$dict.load("intelligentGuardianshipItem",'intelligentGuardianshipItemUnit') | ||||
|     this.getHistory() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .historyList { | ||||
|   font-size: 30px; | ||||
|  | ||||
|   & > div { | ||||
|     padding: 0 32px; | ||||
|     border-bottom: 1px solid #ddd; | ||||
|     height: 96px; | ||||
|     background: #FFFFFF; | ||||
|   } | ||||
|  | ||||
|   .header { | ||||
|     font-size: 32px; | ||||
|   } | ||||
|  | ||||
|   .row { | ||||
|     color: #5AAD6A; | ||||
|  | ||||
|     .abnormal { | ||||
|       color: #CD413A; | ||||
|     } | ||||
|  | ||||
|     & > span { | ||||
|       color: #999; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										279
									
								
								src/pages/guardianship/userDetail.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,279 @@ | ||||
| <template> | ||||
|   <section class="userDetail"> | ||||
|     <div class="selectedInfo"> | ||||
|       <div class="header" flex> | ||||
|         <img :src="`${$cdn}guardianship/tx.png`"/> | ||||
|         <b v-text="detail.name"/> | ||||
|         <div v-if="detail.abnormalStatus==1" class="abnormal">异常</div> | ||||
|         <div class="fill"/> | ||||
|         <make-calls :list="phoneList" :label="`<p>拨打电话</p>`"/> | ||||
|       </div> | ||||
|       <div class="content"> | ||||
|         <ai-cell label="所属地区">{{ detail.areaName }}</ai-cell> | ||||
|         <ai-cell label="联系电话">{{ detail.phone }}</ai-cell> | ||||
|         <ai-cell label="性别">{{ $dict.getLabel('sex', detail.sex) }}</ai-cell> | ||||
|         <ai-cell label="年龄">{{ $calcAge(detail.idNumber) }}</ai-cell> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="card"> | ||||
|       <div flex class="spb header"> | ||||
|         <b v-text="`设备状况`"/> | ||||
|         <span class="onlineStatus" v-html="detail.onlineStatus==1?'设备在线':'<p>设备离线</p>'"/> | ||||
|       </div> | ||||
|       <div flex class="spb wrap quotas"> | ||||
|         <div class="quota" v-for="(op,i) in quotas" :key="i" flex @tap="handleShowHistory(op)"> | ||||
|           <img :src="op.icon"/> | ||||
|           <div class="fill" v-text="op.label"/> | ||||
|           <div :class="{abnormal:op.abnormal}" v-text="op.value"/> | ||||
|           <u-icon name="arrow-right" color="#ddd"/> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="navigation"> | ||||
|         <div class="content spb" flex> | ||||
|           <div flex class="spb wrap"> | ||||
|             <div class="fill" v-text="detail.gpsDesc"/> | ||||
|             <span>最后更新:{{ detail.lastUpdateTime }}</span> | ||||
|             <div class="battery" flex> | ||||
|               <img :src="batteryIcon"/> | ||||
|               <div v-text="`剩余${detail.electricQuantity||0}%`"/> | ||||
|             </div> | ||||
|           </div> | ||||
|           <open-map :data="detail"/> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="card"> | ||||
|       <div flex class="spb header"> | ||||
|         <b v-text="`监护人信息`"/> | ||||
|       </div> | ||||
|       <div flex class="spb guardian" v-for="row in detail.guardians" :key="row.id"> | ||||
|         <span v-text="row.guardianName"/> | ||||
|         <div v-text="row.guardianPhone"/> | ||||
|       </div> | ||||
|     </div> | ||||
|     <ai-back/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import MakeCalls from "./component/makeCalls"; | ||||
| import OpenMap from "./component/openMap"; | ||||
| import AiCell from "../../components/AiCell"; | ||||
| import AiBack from "../../components/AiBack"; | ||||
|  | ||||
| export default { | ||||
|   name: "userDetail", | ||||
|   components: {AiBack, AiCell, OpenMap, MakeCalls}, | ||||
|   computed: { | ||||
|     batteryIcon() { | ||||
|       return this.cdn(this.detail.electricQuantity == 100 ? 'dcm' : 'dcq') | ||||
|     }, | ||||
|     quotas() { | ||||
|       let quota = [ | ||||
|         {key: "0", icon: "1"}, | ||||
|         {key: "1", icon: "2"}, | ||||
|         {key: "2", icon: "3"}, | ||||
|         {key: "3", icon: "4"}, | ||||
|       ] | ||||
|       return quota.map(e => { | ||||
|         let item = this.detail.quota?.find(d => d.item == e.key) | ||||
|         let label = this.$dict.getLabel('intelligentGuardianshipItem', e.key) | ||||
|         return { | ||||
|           label, icon: this.cdn(e.icon), type: e.key, | ||||
|           value: item?.itemValue || "-", | ||||
|           abnormal: item?.abnormalStatus == 1 | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     phoneList() { | ||||
|       let {name: guardianName, phone: guardianPhone} = this.detail | ||||
|       return [{guardianName, guardianPhone}, ...this.detail.guardians] | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       detail: { | ||||
|         guardians: [] | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     cdn(icon) { | ||||
|       return `${this.$cdn}guardianship/${icon}.png` | ||||
|     }, | ||||
|     getDetail(deviceId) { | ||||
|       this.$http.post("/app/appintelligentguardianshipdevice/queryMonitorList", null, { | ||||
|         params: {type: 1, deviceId} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.$set(this.detail, 'quota', res.data.records) | ||||
|         } | ||||
|       }) | ||||
|       this.$http.post("/app/appintelligentguardianshipdevice/queryDetailById", null, { | ||||
|         params: {id: deviceId} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.detail = {...this.detail, ...res.data} | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleShowHistory(item) { | ||||
|       uni.navigateTo({url: `./historyList?type=${item.type}&id=${this.$route.query.id}`}) | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.$dict.load("intelligentGuardianshipItem", 'sex') | ||||
|     this.getDetail(this.$route.query.id) | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .userDetail { | ||||
|   padding-bottom: 60px; | ||||
|  | ||||
|   & > * { | ||||
|     margin-bottom: 16px; | ||||
|   } | ||||
|  | ||||
|   .card { | ||||
|     background: #fff; | ||||
|     font-size: 32px; | ||||
|     box-shadow: 0 1px 1px 0 rgba(221, 221, 221, 1); | ||||
|  | ||||
|     .header { | ||||
|       height: 96px; | ||||
|       background: #FFFFFF; | ||||
|       border-bottom: 1px solid #ddd; | ||||
|     } | ||||
|  | ||||
|     & > .spb { | ||||
|       padding: 0 32px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ::v-deep .selectedInfo { | ||||
|     width: 100%; | ||||
|     background: #fff; | ||||
|     overflow: hidden; | ||||
|     box-sizing: border-box; | ||||
|  | ||||
|     .header { | ||||
|       height: 136px; | ||||
|       font-size: 40px; | ||||
|       padding: 0 32px; | ||||
|       border-bottom: 1px solid #D8DDE6; | ||||
|  | ||||
|       & > img { | ||||
|         width: 82px; | ||||
|         height: 82px; | ||||
|         margin-right: 16px; | ||||
|       } | ||||
|  | ||||
|       .abnormal { | ||||
|         color: #FF4466; | ||||
|         font-size: 24px; | ||||
|         padding: 12px; | ||||
|         background: rgba(#EC4461, .1); | ||||
|         border-radius: 8px; | ||||
|         margin-left: 16px; | ||||
|       } | ||||
|  | ||||
|       span { | ||||
|         margin-left: 0; | ||||
|         color: #999; | ||||
|         font-size: 20px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     & > .content { | ||||
|       padding: 32px; | ||||
|       font-size: 30px; | ||||
|  | ||||
|       ::v-deep .AiCell { | ||||
|         padding: 0; | ||||
|         min-height: 58px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .quotas { | ||||
|     margin-top: 24px; | ||||
|  | ||||
|     .quota { | ||||
|       cursor: pointer; | ||||
|       width: 318px; | ||||
|       height: 84px; | ||||
|       background: #F4F5F6; | ||||
|       border-radius: 8px; | ||||
|       padding: 0 8px 0 24px; | ||||
|       box-sizing: border-box; | ||||
|       margin-bottom: 36px; | ||||
|       font-size: 28px; | ||||
|  | ||||
|       img { | ||||
|         margin-right: 16px; | ||||
|         width: 56px; | ||||
|         height: 56px; | ||||
|       } | ||||
|  | ||||
|       .abnormal { | ||||
|         color: #FF4466; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
|   .navigation { | ||||
|     .content { | ||||
|       padding: 10px 40px 32px; | ||||
|       font-size: 26px; | ||||
|  | ||||
|       & > .spb { | ||||
|         margin-right: 40px; | ||||
|       } | ||||
|  | ||||
|       .fill { | ||||
|         min-width: 100%; | ||||
|         flex-shrink: 0; | ||||
|         margin-bottom: 10px; | ||||
|       } | ||||
|  | ||||
|       span { | ||||
|         margin-left: 0; | ||||
|         color: #999; | ||||
|         font-size: 22px; | ||||
|       } | ||||
|  | ||||
|       .battery > img { | ||||
|         margin-left: 32px; | ||||
|         margin-right: 14px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ::v-deep .onlineStatus { | ||||
|     font-size: 30px; | ||||
|     color: #5AAD6A; | ||||
|  | ||||
|     p { | ||||
|       color: #F5A319; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .guardian { | ||||
|     height: 96px; | ||||
|     border-bottom: 1px solid #ddd; | ||||
|     font-size: 28px; | ||||
|     color: #222; | ||||
|  | ||||
|     &:last-of-type { | ||||
|       border-bottom: none; | ||||
|     } | ||||
|  | ||||
|     span { | ||||
|       color: #666666; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										106
									
								
								src/pages/guardianship/wardList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,106 @@ | ||||
| <template> | ||||
|   <section class="wardList"> | ||||
|     <ai-top-fixed> | ||||
|       <u-search v-model="search" placeholder="请输入姓名" :show-action="false" | ||||
|                 search-icon-color="#ccc" placeholder-color="#999" | ||||
|                 @change="page.current=1,getUser()"/> | ||||
|       <area-selector :areaId="areaId" @select="handleSelectArea"/> | ||||
|     </ai-top-fixed> | ||||
|     <div class="userList"> | ||||
|       <div v-for="row in list" :key="row.id" flex class="row" @tap="handleShowDetail(row)"> | ||||
|         <img :src="top.cdn(row.onlineStatus==1?'zxtx':'lxtx')"/> | ||||
|         <b class="fill" v-text="row.name"/> | ||||
|         <div class="status" :style="{color:$dict.getColor('intelligentGuardianshipAbnormalStatus',row.abnormalStatus)}" | ||||
|              v-text="$dict.getLabel('intelligentGuardianshipAbnormalStatus',row.abnormalStatus)"/> | ||||
|         <u-icon name="arrow-right" color="#ddd"/> | ||||
|       </div> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import AiTopFixed from "../../components/AiTopFixed"; | ||||
| import {mapState} from "vuex"; | ||||
| import AreaSelector from "./component/areaSelector"; | ||||
|  | ||||
| export default { | ||||
|   name: "wardList", | ||||
|   components: {AreaSelector, AiTopFixed}, | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|   }, | ||||
|   inject: ['top'], | ||||
|   data() { | ||||
|     return { | ||||
|       search: "", | ||||
|       areaId: "", | ||||
|       page: {current: 1, size: 20, total: 0}, | ||||
|       list: [] | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getUser() { | ||||
|       let {areaId, search: name} = this | ||||
|       this.$http.post("/app/appintelligentguardianshipdevice/list", null, { | ||||
|         params: {areaId, name, ...this.page} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           let data = res.data.records.reverse() | ||||
|           if (this.page.current > 1) { | ||||
|             this.list = [...this.list, ...data] | ||||
|           } else this.list = data | ||||
|           this.page.total = res.data.total | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleSelectArea({id}) { | ||||
|       this.areaId = id | ||||
|       this.getUser() | ||||
|     }, | ||||
|     reachBottom() { | ||||
|       if (this.page.total > this.list.length) { | ||||
|         this.page.current++ | ||||
|         this.getUser() | ||||
|       } | ||||
|     }, | ||||
|     handleShowDetail(user) { | ||||
|       uni.navigateTo({url: `./userDetail?id=${user.id}`}) | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.areaId = JSON.parse(JSON.stringify(this.user.areaId)) | ||||
|     this.getUser() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .wardList { | ||||
|   background: #f5f5f5; | ||||
|   padding-bottom: 130px; | ||||
|   font-size: 30px; | ||||
|  | ||||
|   ::v-deep .userList { | ||||
|     margin-top: 16px; | ||||
|     background: #fff; | ||||
|  | ||||
|     .row { | ||||
|       font-size: 36px; | ||||
|       margin-left: 32px; | ||||
|       padding-right: 32px; | ||||
|       height: 104px; | ||||
|       border-bottom: 1px solid #ddd; | ||||
|  | ||||
|       img { | ||||
|         width: 72px; | ||||
|         height: 72px; | ||||
|         margin-right: 38px; | ||||
|       } | ||||
|  | ||||
|       .status { | ||||
|         margin-right: 12px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										185
									
								
								src/pages/guardianship/warningDetail.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,185 @@ | ||||
| <template> | ||||
|   <section class="warningDetail"> | ||||
|     <div flex class="header"> | ||||
|       <b v-text="detail.name"/> | ||||
|       <div> | ||||
|         {{ $dict.getLabel('intelligentGuardianshipItem2', detail.item) }} | ||||
|         {{ detail.itemValue }} | ||||
|       </div> | ||||
|     </div> | ||||
|     <ai-map class="fill" :map.sync="amap" :lib.sync="mapLib"/> | ||||
|     <div class="navigation"> | ||||
|       <div class="content spb" flex> | ||||
|         <div flex class="spb wrap"> | ||||
|           <div class="fill" v-text="detail.gpsDesc"/> | ||||
|           <span>最后更新:{{ detail.createTime }}</span> | ||||
|         </div> | ||||
|         <open-map :data="detail"/> | ||||
|       </div> | ||||
|     </div> | ||||
|     <make-calls :list="phoneList"> | ||||
|       <div class="bottomBtn">拨打电话</div> | ||||
|     </make-calls> | ||||
|     <ai-back/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import OpenMap from "./component/openMap"; | ||||
| import MakeCalls from "./component/makeCalls"; | ||||
| import AiBack from "../../components/AiBack"; | ||||
| import AiMap from "../../components/AiMap"; | ||||
|  | ||||
| export default { | ||||
|   name: "warningDetail", | ||||
|   components: {AiMap, AiBack, MakeCalls, OpenMap}, | ||||
|   computed: { | ||||
|     phoneList() { | ||||
|       let {name: guardianName, phone: guardianPhone} = this.detail | ||||
|       return [{guardianName, guardianPhone}, ...this.detail.guardians] | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       detail: {guardians: []}, | ||||
|       mapLib: null, | ||||
|       amap: null, | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getDetail(id) { | ||||
|       return this.$http.post("/app/appintelligentguardianshipalarm/queryDetailById", null, { | ||||
|         params: {id}, | ||||
|         withoutToken: true | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.detail = res.data | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     initMap() { | ||||
|       if (this.mapLib) { | ||||
|         let pos = new this.mapLib.LngLat(this.detail.lng, this.detail.lat) | ||||
|         this.amap.add(new this.mapLib.Marker({ | ||||
|           position: pos, | ||||
|           anchor: 'bottom-center', | ||||
|           content: `<div class="marker">${this.detail.name}</div>`, | ||||
|         })) | ||||
|         this.amap.setFitView() | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     mapLib(v) { | ||||
|       this.detail.id && v && this.initMap() | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.$dict.load("intelligentGuardianshipItem",'intelligentGuardianshipItem2', 'sex') | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.getDetail(this.$route.query.id).then(() => this.initMap()) | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .warningDetail { | ||||
|   padding: 48px 48px 112px; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   box-sizing: border-box; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   position: absolute; | ||||
|  | ||||
|   .header { | ||||
|     font-size: 40px; | ||||
|     font-weight: bold; | ||||
|     color: #333333; | ||||
|     justify-content: center; | ||||
|     margin-bottom: 48px; | ||||
|  | ||||
|     & > div { | ||||
|       color: #EC4461; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .navigation { | ||||
|     .content { | ||||
|       padding: 10px 0 32px; | ||||
|       font-size: 28px; | ||||
|       color: #555; | ||||
|  | ||||
|       & > .spb { | ||||
|         margin-right: 40px; | ||||
|       } | ||||
|  | ||||
|       .fill { | ||||
|         min-width: 100%; | ||||
|         flex-shrink: 0; | ||||
|         margin-bottom: 10px; | ||||
|       } | ||||
|  | ||||
|       span { | ||||
|         margin-left: 0; | ||||
|         color: #999; | ||||
|         font-size: 22px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .bottomBtn { | ||||
|     position: fixed; | ||||
|     left: 0; | ||||
|     bottom: 0; | ||||
|     width: 100%; | ||||
|     text-align: center; | ||||
|     color: #fff; | ||||
|     background: #1365DD; | ||||
|     line-height: 112px; | ||||
|   } | ||||
|  | ||||
|   .AiMap { | ||||
|     margin-bottom: 72px; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   ::v-deep .marker { | ||||
|     border-radius: 52px; | ||||
|     background: #F46159; | ||||
|     color: #fff; | ||||
|     font-size: 22px; | ||||
|     padding: 4px 16px; | ||||
|     white-space: nowrap; | ||||
|     position: relative; | ||||
|  | ||||
|     &:before { | ||||
|       position: absolute; | ||||
|       left: calc(50% - 30px); | ||||
|       bottom: -34px; | ||||
|       z-index: -1; | ||||
|       width: 60px; | ||||
|       height: 60px; | ||||
|       border-radius: 50%; | ||||
|       background-color: #F46159; | ||||
|       animation: mapWarn 1s ease-out 0s infinite; | ||||
|       content: " "; | ||||
|     } | ||||
|  | ||||
|     &:after { | ||||
|       position: absolute; | ||||
|       display: block; | ||||
|       content: " "; | ||||
|       bottom: -8px; | ||||
|       left: 50%; | ||||
|       transform: translateX(-50%); | ||||
|       border: 8px solid transparent; | ||||
|       border-bottom: none; | ||||
|       border-top-color: #F46159; | ||||
|       height: 0; | ||||
|       width: 0; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										184
									
								
								src/pages/interview/detail.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,184 @@ | ||||
| <template> | ||||
|   <div class="interviewDetail"> | ||||
|     <template v-if="isEdit"> | ||||
|       <u-form ref="interviewForm" label-position="top" :rules="rules" :model="form"> | ||||
|         <u-form-item label="调查走访事项" prop="title" required> | ||||
|           <u-input v-model="form.title" placeholder="请输入,最多30字" maxlength="30"/> | ||||
|         </u-form-item> | ||||
|         <u-form-item label="调查走访内容" prop="content"> | ||||
|           <ai-textarea v-model="form.content" placeholder="请输入,最多500字" :maxlength="500"/> | ||||
|         </u-form-item> | ||||
|         <u-form-item label="图片(最多9张)"> | ||||
|           <ai-uploader multiple :limit="9" :def.sync="form.fileList" action="/admin/file/add2"/> | ||||
|         </u-form-item> | ||||
|       </u-form> | ||||
|       <div bottom> | ||||
|         <u-button type="primary" @tap="submitForm">保存</u-button> | ||||
|       </div> | ||||
|     </template> | ||||
|     <template v-else> | ||||
|       <div class="headerPane"> | ||||
|         <b>{{ form.title }}</b> | ||||
|         <div>记录时间:{{ form.createTime }}</div> | ||||
|       </div> | ||||
|       <div class="contentPane"> | ||||
|         <div v-html="form.content"/> | ||||
|         <div flex class="wrap"> | ||||
|           <ai-image v-for="(op,i) in form.fileList" :src="op.accessUrl" preview :key="i"/> | ||||
|         </div> | ||||
|       </div> | ||||
|     </template> | ||||
|     <ai-back/> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import UButton from "../../uview/components/u-button/u-button"; | ||||
| import AiUploader from "../../components/AiUploader"; | ||||
| import UInput from "../../uview/components/u-input/u-input"; | ||||
| import AiImage from "../../components/AiImage"; | ||||
| import AiTextarea from "../../components/AiTextarea"; | ||||
| import UFormItem from "../../uview/components/u-form-item/u-form-item"; | ||||
| import AiBack from "../../components/AiBack"; | ||||
|  | ||||
| export default { | ||||
|   name: 'interviewDetail', | ||||
|   components: {AiBack, UFormItem, AiTextarea, AiImage, UInput, AiUploader, UButton}, | ||||
|   computed: { | ||||
|     isEdit() { | ||||
|       let flag = this.$route.query?.detail != 1 | ||||
|       !flag && uni.setNavigationBarTitle({title: "走访详情"}) | ||||
|       return flag | ||||
|     }, | ||||
|     rules() { | ||||
|       return { | ||||
|         title: [{required: true, message: '请输入 调查走访事项'}], | ||||
|         // content: [{required: true, message: '请输入 调查走访内容'}], | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       form: { | ||||
|         fileList: [] | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.searchDetail(); | ||||
|   }, | ||||
|   methods: { | ||||
|     submitForm() { | ||||
|       this.$refs.interviewForm?.validate(v => { | ||||
|         if (v) { | ||||
|           this.$http.post(`/app/appinterview/add-xcx`, { | ||||
|             ...this.form | ||||
|           }).then(res => { | ||||
|             if (res?.code == 0) { | ||||
|               this.$u.toast("提交成功!") | ||||
|               uni.navigateBack() | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     searchDetail() { | ||||
|       let {id} = this.$route.query | ||||
|       id && this.$http.post(`/app/appinterview/queryDetailById`, null, { | ||||
|         params: {id} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.form = {...res.data}; | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .interviewDetail { | ||||
|   background: #F3F6F9; | ||||
|   min-height: 100%; | ||||
|  | ||||
|   .u-form { | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     overflow-y: auto; | ||||
|     background-color: #f3f6f9; | ||||
|     position: relative; | ||||
|     padding: 0 0 188px; | ||||
|     box-sizing: border-box; | ||||
|     font-size: 30px; | ||||
|  | ||||
|     ::v-deep textarea { | ||||
|       width: 100%; | ||||
|     } | ||||
|  | ||||
|     ::v-deep .u-form-item { | ||||
|       margin-bottom: 16px; | ||||
|  | ||||
|       .u-form-item--left__content__label { | ||||
|         font-weight: 400; | ||||
|       } | ||||
|  | ||||
|       div[flex] { | ||||
|         width: 100%; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   div[bottom] { | ||||
|     z-index: 99; | ||||
|     padding: 0; | ||||
|     height: 112px; | ||||
|  | ||||
|     .u-btn { | ||||
|       height: 100%; | ||||
|       border-radius: 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ::v-deep .headerPane { | ||||
|     width: 100%; | ||||
|     background: #3975C6; | ||||
|     color: #fff; | ||||
|     padding: 24px 32px 32px; | ||||
|     box-sizing: border-box; | ||||
|     font-size: 28px; | ||||
|  | ||||
|     b { | ||||
|       display: block; | ||||
|       font-size: 40px; | ||||
|       line-height: 64px; | ||||
|       letter-spacing: 2px; | ||||
|       margin-bottom: 16px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ::v-deep .contentPane { | ||||
|     padding: 32px; | ||||
|     width: 100%; | ||||
|     box-sizing: border-box; | ||||
|     font-size: 32px; | ||||
|     font-weight: 400; | ||||
|     color: #666; | ||||
|     line-height: 56px; | ||||
|  | ||||
|     .wrap { | ||||
|       margin-top: 32px; | ||||
|     } | ||||
|  | ||||
|     .AiImage { | ||||
|       width: 31%; | ||||
|       margin-bottom: 16px; | ||||
|       margin-right: 16px; | ||||
|  | ||||
|       image { | ||||
|         width: 100%; | ||||
|         height: 218px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										224
									
								
								src/pages/interview/interview.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,224 @@ | ||||
| <template> | ||||
|   <div class="interview"> | ||||
|     <ai-top-fixed> | ||||
|       <div flex> | ||||
|         <ai-date placeholder="日期选择" mode="range" @change="handleDateSearch"/> | ||||
|         <u-search placeholder="请输入标题" :show-action="false" v-model="search.title" @search="current=1,getList()"/> | ||||
|       </div> | ||||
|     </ai-top-fixed> | ||||
|     <template v-if="list.length>0"> | ||||
|       <ai-card v-for="(e,index) in list" :key="index" @click.native="goDetail(e.id,1)"> | ||||
|         <template #custom> | ||||
|           <div flex> | ||||
|             <b class="fill">{{ e.title }}</b> | ||||
|           </div> | ||||
|           <div flex v-if="!!e.fileList" class="wrap"> | ||||
|             <ai-image v-for="(op,i) in e.fileList.slice(0,3)" :src="op.accessUrl" preview :key="i"/> | ||||
|           </div> | ||||
|           <div class="bottom">{{ e.createTime }}</div> | ||||
|         </template> | ||||
|         <template #menu> | ||||
|           <div class="menu" @tap.stop="goDetail(e.id)">编辑</div> | ||||
|           <div class="menu" @tap.stop="handleDelete(e.id)">删除</div> | ||||
|         </template> | ||||
|       </ai-card> | ||||
|       <u-loadmore :status="loadmore" color="#999" font-size="24" | ||||
|                   margin-top="32" margin-bottom="80"/> | ||||
|     </template> | ||||
|     <div class="no-message" v-else> | ||||
|       <image src="https://cdn.cunwuyun.cn/wxAdmin/img/message.png"/> | ||||
|       <p>您还未添加过入户调查走访<br>点击<b>新增按钮</b>试试吧~</p> | ||||
|     </div> | ||||
|     <ai-fixed-btn> | ||||
|       <div class="addBtn iconfont iconfont-iconfangda" @tap="gotoAdd()"/> | ||||
|     </ai-fixed-btn> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import AiSelect from "../../components/AiSelect"; | ||||
| import AiTopFixed from "../../components/AiTopFixed"; | ||||
| import AiCard from "../../components/AiCard"; | ||||
| import AiImage from "../../components/AiImage"; | ||||
| import AiDate from "../../components/AiDate"; | ||||
| import AiFixedBtn from "../../components/AiFixedBtn"; | ||||
|  | ||||
| export default { | ||||
|   name: "interview", | ||||
|   components: {AiFixedBtn, AiDate, AiImage, AiCard, AiTopFixed, AiSelect}, | ||||
|   data() { | ||||
|     return { | ||||
|       search: {title: ""}, | ||||
|       list: [], | ||||
|       current: 1, | ||||
|       pages: 0 | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     loadmore() { | ||||
|       return this.pages <= this.current ? 'loading ' : 'nomore' | ||||
|     } | ||||
|   }, | ||||
|   onShow() { | ||||
|     this.current = 1; | ||||
|     this.getList() | ||||
|   }, | ||||
|   onReachBottom() { | ||||
|     this.current++; | ||||
|     this.getList() | ||||
|   }, | ||||
|   methods: { | ||||
|     getList() { | ||||
|       this.$http.post('/app/appinterview/list-xcx', null, { | ||||
|         params: { | ||||
|           current: this.current, | ||||
|           size: 10, | ||||
|           ...this.search | ||||
|         } | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.list = this.current > 1 ? [...this.list, ...res.data.records] : res.data.records | ||||
|           this.pages = res.data.pages | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     goDetail(id, readonly) { | ||||
|       let url = `./detail?id=${id}` | ||||
|       readonly && (url += "&detail=1") | ||||
|       uni.navigateTo({url}) | ||||
|     }, | ||||
|     gotoAdd() { | ||||
|       uni.navigateTo({url: `./detail`}) | ||||
|     }, | ||||
|     handleDelete(ids) { | ||||
|       this.$confirm("是否要删除该调查走访").then(() => { | ||||
|         this.$http.post("/app/appinterview/delete", null, { | ||||
|           params: {ids} | ||||
|         }).then(res => { | ||||
|           if (res?.code == 0) { | ||||
|             this.$u.toast("删除成功!") | ||||
|             this.getList() | ||||
|           } | ||||
|         }) | ||||
|       }) | ||||
|     }, | ||||
|     handleDateSearch(v) { | ||||
|       this.search.startTime = v.startDate | ||||
|       this.search.endTime = v.endDate || v.startDate | ||||
|       this.current = 1 | ||||
|       this.getList() | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .interview { | ||||
|   width: 100%; | ||||
|   min-height: 100%; | ||||
|   box-sizing: border-box; | ||||
|   position: absolute; | ||||
|   background: #fff; | ||||
|  | ||||
|   .no-message { | ||||
|     margin-top: 140px; | ||||
|     text-align: center; | ||||
|     color: #888; | ||||
|     font-size: 30px; | ||||
|  | ||||
|     b { | ||||
|       font-size: 32px; | ||||
|       color: $uni-color-primary; | ||||
|       padding: 0 8px; | ||||
|     } | ||||
|  | ||||
|     image { | ||||
|       width: 320px; | ||||
|       height: 240px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ::v-deep .AiCard { | ||||
|     width: 100%; | ||||
|     min-height: 160px; | ||||
|     background: #FFFFFF; | ||||
|     padding: 32px 32px 0; | ||||
|     box-sizing: border-box; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     position: relative; | ||||
|  | ||||
|     b { | ||||
|       display: block; | ||||
|       width: 100%; | ||||
|       font-size: 30px; | ||||
|       white-space: nowrap; | ||||
|       overflow: hidden; | ||||
|       text-overflow: ellipsis; | ||||
|       color: #333; | ||||
|       margin-right: 60px; | ||||
|       margin-bottom: 20px; | ||||
|     } | ||||
|  | ||||
|     .iconfont-iconMore { | ||||
|       color: #666; | ||||
|       position: absolute; | ||||
|       right: 32px; | ||||
|       top: 32px; | ||||
|     } | ||||
|  | ||||
|     .bottom { | ||||
|       font-size: 24px; | ||||
|       color: #999999; | ||||
|       padding: 24px 0; | ||||
|       border-bottom: 1px solid rgba(221, 221, 221, .4); | ||||
|     } | ||||
|  | ||||
|     .AiImage { | ||||
|       width: 30%; | ||||
|       margin-bottom: 8px; | ||||
|       margin-right: 8px; | ||||
|  | ||||
|       image { | ||||
|         width: 100%; | ||||
|         height: 218px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .addBtn { | ||||
|     width: 96px; | ||||
|     height: 96px; | ||||
|     flex-shrink: 0; | ||||
|     background: $uni-color-primary; | ||||
|     box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2); | ||||
|     font-size: 48px; | ||||
|     color: #fff; | ||||
|     border-radius: 50%; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
|     display: flex; | ||||
|   } | ||||
|  | ||||
|   .menu { | ||||
|     text-align: center; | ||||
|     line-height: 80px; | ||||
|     width: 192px; | ||||
|     height: 80px; | ||||
|     font-size: 28px; | ||||
|     font-weight: 400; | ||||
|     color: #333333; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .u-search { | ||||
|     margin-bottom: 0 !important; | ||||
|     padding-left: 146px; | ||||
|     box-shadow: none; | ||||
|  | ||||
|     .u-content { | ||||
|       padding-left: 50px; | ||||
|       box-sizing: border-box; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										174
									
								
								src/pages/loading.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,174 @@ | ||||
| <template> | ||||
|   <section class="loading"> | ||||
|     <!--    <div class="iconfont iconfont-iconWeChat"/>--> | ||||
|     <!--    <div class="iconfont iconfont-iconjuminxinxi"/>--> | ||||
|     <!--    <div class="iconfont iconfont-iconLogo"/>--> | ||||
|     <ai-result v-if="result.tips" v-bind="result"/> | ||||
|     <template v-if="isDev"> | ||||
|       <input v-if="!!$route.query.code" class="codeText" :value="$route.query.code"/> | ||||
|       <div class="codeBtn" @click="devGetCode">获取code</div> | ||||
|       <div flex class="appsPane wrap"> | ||||
|         <b v-for="app in apps" :key="app.key" @tap="gotoApp(app.key)">{{ app.name }}</b> | ||||
|       </div> | ||||
|     </template> | ||||
|  | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapActions, mapState} from 'vuex' | ||||
| import AiResult from "../components/AiResult"; | ||||
| import UTag from "../uview/components/u-tag/u-tag"; | ||||
|  | ||||
| export default { | ||||
|   name: 'loading', | ||||
|   components: {UTag, AiResult}, | ||||
|   inject: ['root'], | ||||
|   computed: { | ||||
|     ...mapState(['token', 'apps', 'openUser', 'user']), | ||||
|     currentApp() { | ||||
|       return this.apps.find(e => e.key == this.$route.query.app) || {} | ||||
|     }, | ||||
|     isDev() { | ||||
|       return this.$route.hash == "#dev" | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       result: {} | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     ...mapActions(['getToken', 'getAccount', 'agentSign', 'getUserInfo', 'getCode', 'closeAgent']), | ||||
|     initAccess() { | ||||
|       if (this.$route.hash == "#error" || this.isDev) { | ||||
|         return Promise.resolve() | ||||
|       } else if (this.$route.hash == "#form") { | ||||
|         if (this.openUser?.openId || !!this.$route.query.preview) { | ||||
|           this.openForm() | ||||
|         } else if (this.$route.query?.code) { | ||||
|           this.getToken(this.$route.query?.code) | ||||
|           .then(() => this.getUserInfo()) | ||||
|           .then(() => this.openForm()) | ||||
|         } else this.getCode(location.href) | ||||
|       } else if (this.token) {//获取账号信息 | ||||
|         return this.getAccount() | ||||
|       } else if (this.$route.query?.code) {//获取token | ||||
|         return this.getToken(this.$route.query?.code) | ||||
|       } else {//获取应用配置 | ||||
|         this.getCode(location.href) | ||||
|       } | ||||
|     }, | ||||
|     openForm() { | ||||
|       this.redirectTo("/askForm/askForm") | ||||
|     }, | ||||
|     redirectTo(path) { | ||||
|       let {query, hash} = this.$route | ||||
|       delete query.app | ||||
|       uni.redirectTo({ | ||||
|         url: `/pages${path}`, success: () => { | ||||
|           this.$router.push({query, hash}) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     gotoApp(app) { | ||||
|       uni.reLaunch({url: '/pages/loading?app=' + app}) | ||||
|     }, | ||||
|     devGetCode() { | ||||
|       this.getCode(location.origin + '/pages/loading#dev') | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     uni.showLoading({ | ||||
|       title: "加载中" | ||||
|     }) | ||||
|     this.initAccess()?.then(() => { | ||||
|       uni.hideLoading() | ||||
|       if (this.token) { | ||||
|         if (this.currentApp.name) { | ||||
|           this.redirectTo(this.currentApp.path) | ||||
|         } else if (this.$route.query.url) { | ||||
|           this.redirectTo(this.$route.query.url) | ||||
|         } else { | ||||
|           this.result = { | ||||
|             status: "error", | ||||
|             tips: "应用加载失败", | ||||
|             btn: "重新加载", | ||||
|             btnTap() { | ||||
|               location.href = location.href?.replace("#error", '') | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } else if (this.isDev) { | ||||
|         this.result = { | ||||
|           tips: "欢迎进入开发应用", | ||||
|         } | ||||
|       } else { | ||||
|         this.result = { | ||||
|           status: "error", | ||||
|           tips: "应用加载失败", | ||||
|           btn: "重新加载", | ||||
|           btnTap() { | ||||
|             location.href = location.href?.replace("#error", '') | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     })?.catch(() => { | ||||
|       uni.navigateTo({url: './login'}) | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .loading { | ||||
|   height: 100%; | ||||
|   position: relative; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   align-items: center; | ||||
|  | ||||
|   & > span { | ||||
|     font-size: 36px; | ||||
|   } | ||||
|  | ||||
|   .codeText { | ||||
|     font-size: 24px; | ||||
|     margin-top: 16px; | ||||
|     padding: 4px 0; | ||||
|     width: 100%; | ||||
|     text-align: center; | ||||
|     background: #ccc; | ||||
|   } | ||||
|  | ||||
|   .codeBtn { | ||||
|     width: 180px; | ||||
|     text-align: center; | ||||
|     cursor: pointer; | ||||
|     font-size: 24px; | ||||
|     background: $uni-color-warning; | ||||
|     color: #fff; | ||||
|     padding: 8px; | ||||
|     margin-top: 16px; | ||||
|     border-radius: 8px; | ||||
|     font-weight: normal; | ||||
|   } | ||||
|  | ||||
|   .appsPane { | ||||
|     justify-content: center; | ||||
|     margin-top: 16px; | ||||
|     padding: 0 16px; | ||||
|  | ||||
|     b { | ||||
|       cursor: pointer; | ||||
|       font-size: 24px; | ||||
|       background: $uni-color-primary; | ||||
|       color: #fff; | ||||
|       padding: 8px; | ||||
|       margin: 4px; | ||||
|       border-radius: 8px; | ||||
|       font-weight: normal; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										96
									
								
								src/pages/login.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,96 @@ | ||||
| <template> | ||||
|   <section class="login"> | ||||
|     <u-form :model="form" ref="loginForm" label-width="140"> | ||||
|       <u-form-item label="账号" prop="phone" :errory-type="['message']"> | ||||
|         <u-input v-model="form.phone" placeholder="请输入手机号"/> | ||||
|       </u-form-item> | ||||
|       <u-form-item label="验证码" prop="vcode"> | ||||
|         <u-input v-model="form.vcode" placeholder="请输入短信验证码"/> | ||||
|         <u-verification-code ref="vcode" secords="60" @change="v=>tips=v"/> | ||||
|         <div class="vcode" @tap="$u.debounce(getVCode)">{{ tips }}</div> | ||||
|       </u-form-item> | ||||
|     </u-form> | ||||
|     <div bottom> | ||||
|       <u-button type="primary" @tap="handleLogin">绑定并登录</u-button> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapActions, mapState} from "vuex"; | ||||
|  | ||||
| export default { | ||||
|   name: "login", | ||||
|   inject: ['root'], | ||||
|   computed: { | ||||
|     ...mapState(['lastPage']), | ||||
|     rules() { | ||||
|       return { | ||||
|         phone: [{required: true, message: "请选择分组"}], | ||||
|         vcode: [{required: true, message: "请选择快捷回复类型"}], | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       form: {}, | ||||
|       tips: '' | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     ...mapActions(['getCode']), | ||||
|     getVCode() { | ||||
|       if (this.form.phone) { | ||||
|         this.$http.post("/admin/user/sendCode", null, { | ||||
|           withoutToken: 1, | ||||
|           params: {phone: this.form.phone} | ||||
|         }).then(() => { | ||||
|           this.$u.toast("验证已发送!") | ||||
|           this.$refs.vcode?.start() | ||||
|         }) | ||||
|       } else { | ||||
|         this.$u.toast("请先填写手机号!") | ||||
|       } | ||||
|     }, | ||||
|     handleLogin() { | ||||
|       this.$refs.loginForm.validate(v => { | ||||
|         if (v) { | ||||
|           let params = { | ||||
|             ...this.form, code: this.$route.query?.code, then: res => { | ||||
|               let last = uni.getStorageSync("lastApp") | ||||
|               if (last) { | ||||
|                 this.$store.commit("login", [res?.token_type, res?.access_token].join(" ").trim()) | ||||
|                 uni.removeStorageSync("lastApp") | ||||
|                 // this.root.getCode(location.origin + last) | ||||
|                 uni.reLaunch({url: "./loading?app=" + last}) | ||||
|               } else this.$u.toast("绑定成功,请重新打开应用页面!") | ||||
|             } | ||||
|           } | ||||
|           this.$store.commit("bindAccount", params) | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     !this.$route.query?.code && this.getCode() | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.$nextTick(() => this.$refs.loginForm?.setRules(this.rules)) | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .login { | ||||
|   border-top: 1px solid #D4D4D4; | ||||
|   padding: 0 0 208px; | ||||
|   background: #F5F5F5; | ||||
|   box-sizing: border-box; | ||||
|   height: 100%; | ||||
|  | ||||
|   .vcode { | ||||
|     color: $uni-color-primary; | ||||
|     cursor: pointer; | ||||
|   } | ||||
| } | ||||
| </style> | ||||