Compare commits
	
		
			8 Commits
		
	
	
		
			ba8dc41c83
			...
			feature/nu
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 17bf8599d1 | ||
|  | 45017af56d | ||
|  | 2f93572701 | ||
|  | 64f08839e2 | ||
|  | d52cbffed7 | ||
|  | 0e7cb83bd4 | ||
|  | bc8b161020 | ||
|  | d2e63c2588 | 
							
								
								
									
										29
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -27,3 +27,32 @@ yarn-error.log* | ||||
| /project/*/dist | ||||
| /ui/package-lock.json | ||||
| /examples/modules.json | ||||
|  | ||||
|  | ||||
| /src/apps/ | ||||
| /src/config.json | ||||
| /.nuxt/components/nuxt.js | ||||
| /.nuxt/components/nuxt-build-indicator.vue | ||||
| /.nuxt/components/nuxt-child.js | ||||
| /.nuxt/components/nuxt-error.vue | ||||
| /.nuxt/components/nuxt-link.client.js | ||||
| /.nuxt/components/nuxt-link.server.js | ||||
| /.nuxt/components/nuxt-loading.vue | ||||
| /.nuxt/layouts/default.vue | ||||
| /.nuxt/mixins/fetch.client.js | ||||
| /.nuxt/mixins/fetch.server.js | ||||
| /.nuxt/views/app.template.html | ||||
| /.nuxt/views/error.html | ||||
| /.nuxt/App.js | ||||
| /.nuxt/client.js | ||||
| /.nuxt/empty.js | ||||
| /.nuxt/index.js | ||||
| /.nuxt/jsonp.js | ||||
| /.nuxt/loading.html | ||||
| /.nuxt/middleware.js | ||||
| /.nuxt/router.js | ||||
| /.nuxt/router.scrollBehavior.js | ||||
| /.nuxt/routes.json | ||||
| /.nuxt/server.js | ||||
| /.nuxt/store.js | ||||
| /.nuxt/utils.js | ||||
|   | ||||
							
								
								
									
										5
									
								
								.npmrc
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								.npmrc
									
									
									
									
									
								
							| @@ -1,5 +1,4 @@ | ||||
| registry=http://192.168.1.87:4873/ | ||||
| email=aixianling@sinoecare.com | ||||
| always-auth=true | ||||
| package-lock=false | ||||
| //192.168.1.87:4873/:_auth="YWRtaW46YWRtaW4xMjM=" | ||||
| registry=http://registry.npmmirror.com | ||||
| legacy-peer-deps=true | ||||
|   | ||||
| @@ -102,7 +102,7 @@ export default { | ||||
|       localStorage.setItem("searchApp", this.searchApp) | ||||
|     }, | ||||
|   }, | ||||
|   created() { | ||||
|   mounted() { | ||||
|     this.searchApp = localStorage.getItem("searchApp") || "" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,18 +1,14 @@ | ||||
| import Vue from 'vue'; | ||||
| import App from './App.vue'; | ||||
| import ui from 'element-ui'; | ||||
| import router from './router/router'; | ||||
| import axios from './router/axios'; | ||||
| import utils from './utils'; | ||||
| import dui from 'dui'; | ||||
| import store from './store'; | ||||
| import dataV from '@jiaminghi/data-view'; | ||||
| import dvui from '@dui/dv' | ||||
|  | ||||
| Vue.use(dataV) | ||||
| Vue.use(ui); | ||||
| Vue.use(dui); | ||||
| Vue.use(dvui); | ||||
| //富文本编辑器配置 | ||||
| Vue.config.productionTip = false; | ||||
| Object.keys(utils).map((e) => (Vue.prototype[e] = utils[e])); | ||||
| @@ -28,7 +24,6 @@ store.dispatch('getSystem').then(res => { | ||||
|   Vue.prototype.$theme = theme?.web || "blue" | ||||
|   return import(`dui/lib/styles/theme.${theme?.web}.scss`).catch(() => 0) | ||||
| }).finally(() => { | ||||
|   Vue.prototype.$vm = app | ||||
|   import(`dui/lib/styles/common.scss`).finally(() => app.$mount('#app')) | ||||
| }) | ||||
|  | ||||
|   | ||||
							
								
								
									
										21
									
								
								examples/nuxt.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								examples/nuxt.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| import {resolve} from 'path' | ||||
|  | ||||
| export default { | ||||
|   css: ['ui/lib/styles/common.scss'], | ||||
|   dev: process.env.NODE_ENV !== 'production', | ||||
|   alias: { | ||||
|     'style': resolve(__dirname, './assets/style'), | ||||
|     'dui': resolve(__dirname, '../ui') | ||||
|   }, | ||||
|   srcDir: "examples", | ||||
|   dir: { | ||||
|     pages: 'views', | ||||
|   }, | ||||
|   build: { | ||||
|     postcss: null, | ||||
|     transpile: [/^ui/] | ||||
|   }, | ||||
|   plugins: [ | ||||
|     '~plugins/ui.js' | ||||
|   ], | ||||
| } | ||||
							
								
								
									
										7
									
								
								examples/plugins/ui.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								examples/plugins/ui.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| import Vue from 'vue'; | ||||
| import ElementUI from 'element-ui'; | ||||
| import dui from 'dui/packages'; | ||||
| import 'dui/lib/styles/common.scss'; | ||||
|  | ||||
| Vue.use(ElementUI); | ||||
| Vue.use(dui) | ||||
| @@ -1,26 +1,26 @@ | ||||
| import Vue from 'vue' | ||||
| import Vuex from 'vuex' | ||||
| import preState from 'vuex-persistedstate' | ||||
| import * as modules from "dui/lib/js/modules" | ||||
| import xsActions from "../../project/xiushan/actions" | ||||
|  | ||||
| Vue.use(Vuex) | ||||
|  | ||||
| export default new Vuex.Store({ | ||||
|   state: { | ||||
|     apps: [] | ||||
|   }, | ||||
|   mutations: { | ||||
|     addApp(state, app) { | ||||
|       state.apps.push(app) | ||||
|     }, | ||||
|     cleanApps(state) { | ||||
|       state.apps = [] | ||||
|     }, | ||||
|   }, | ||||
|   actions: { | ||||
|     ...xsActions | ||||
|   }, | ||||
|   modules, | ||||
|   plugins: [preState()] | ||||
| export const state = () => ({ | ||||
|   apps: [] | ||||
| }) | ||||
| export const mutations = { | ||||
|   addApp(state, app) { | ||||
|     state.apps.push(app) | ||||
|   }, | ||||
|   cleanApps(state) { | ||||
|     state.apps = [] | ||||
|   } | ||||
| } | ||||
| const actions = { | ||||
|   ...xsActions | ||||
| } | ||||
| export default { | ||||
|   state, | ||||
|   mutations, | ||||
|   actions, | ||||
|   modules, | ||||
| } | ||||
|   | ||||
| @@ -6,9 +6,7 @@ | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|  | ||||
| import {mapState} from "vuex"; | ||||
| import Vue from "vue"; | ||||
|  | ||||
| export default { | ||||
|   name: "appEntry", | ||||
| @@ -20,14 +18,6 @@ export default { | ||||
|       return app.esm ?? "" | ||||
|     } | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.$vm.$on("mock", v => { | ||||
|       if (!!this.$refs.currentPage.$children?.[0]?.form) { | ||||
|         this.$refs.currentPage.$children[0].form = v | ||||
|         this.$refs.currentPage.$children[0].$forceUpdate() | ||||
|       } | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
|   | ||||
| @@ -25,16 +25,15 @@ | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import SliderNav from "./components/sliderNav"; | ||||
| import MainContent from "./components/mainContent"; | ||||
| import HeaderNav from "./components/headerNav"; | ||||
| import SliderNav from "../components/sliderNav"; | ||||
| import MainContent from "../components/mainContent"; | ||||
| import HeaderNav from "../components/headerNav"; | ||||
| import {mapActions, mapMutations, mapState} from "vuex"; | ||||
| import Mock from "./components/mock"; | ||||
| import AiDvWrapper from "@dui/dv/layout/AiDvWrapper/AiDvWrapper.vue"; | ||||
| import Mock from "../components/mock"; | ||||
| 
 | ||||
| export default { | ||||
|   name: 'app', | ||||
|   components: {AiDvWrapper, Mock, HeaderNav, MainContent, SliderNav}, | ||||
|   components: {Mock, HeaderNav, MainContent, SliderNav}, | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|     login() { | ||||
| @@ -75,8 +74,9 @@ export default { | ||||
|     handleMock() { | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     wx = jWeixin | ||||
|   mounted() { | ||||
|     const {jWeixin} = window | ||||
|     window.wx = jWeixin | ||||
|     if (this.user.token) this.getUserInfo().finally(() => { | ||||
|       if (/^\/project\/xiushan/.test(location.pathname)) { | ||||
|         this.getFinanceUser() | ||||
							
								
								
									
										35
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,38 +1,28 @@ | ||||
| { | ||||
|   "name": "dvcp-web-apps", | ||||
|   "version": "3.0.0", | ||||
|   "name": "dvcp-web", | ||||
|   "version": "4.0.0", | ||||
|   "private": false, | ||||
|   "author": "kubbo", | ||||
|   "scripts": { | ||||
|     "dev": "vue-cli-service serve examples/main.js", | ||||
|     "dev": "nuxt -c examples/nuxt.config.js", | ||||
|     "build": "vue-cli-service build", | ||||
|     "dev:ai": "vue-cli-service serve examples/main.js --mode ai", | ||||
|     "dev:oms": "vue-cli-service serve examples/main.js --mode oms", | ||||
|     "dev:biaopin": "vue-cli-service serve examples/main.js --mode biaopin", | ||||
|     "dev:dv": "vue-cli-service serve examples/main.js --mode dv", | ||||
|     "dev:fengdu": "vue-cli-service serve examples/main.js --mode fengdu", | ||||
|     "lib": "npm publish||(npm unpublish -f&&npm publish)", | ||||
|     "preui": "npm publish -ws||(npm unpublish -f -ws&&npm publish -ws)", | ||||
|     "ui": "npm i dui@latest @dui/dv@latest", | ||||
|     "sync": "node bin/appsSync.js", | ||||
|     "preview": "vue-cli-service serve" | ||||
|     "preview": "vue-cli-service serve", | ||||
|     "prebuild": "" | ||||
|   }, | ||||
|   "workspaces": [ | ||||
|     "ui", | ||||
|     "ui/dv" | ||||
|   ], | ||||
|   "files": [ | ||||
|     "packages", | ||||
|     "project" | ||||
|   ], | ||||
|   "dependencies": { | ||||
|     "@amap/amap-jsapi-loader": "^1.0.1", | ||||
|     "@dui/dv": "^1.0.0", | ||||
|     "@ckeditor/ckeditor5-vue2": "^3.0.1", | ||||
|     "@jiaminghi/data-view": "^2.10.0", | ||||
|     "@logicflow/core": "^1.2.1", | ||||
|     "bin-ace-editor": "^3.2.0", | ||||
|     "crypto-js": "^4.1.1", | ||||
|     "dayjs": "^1.8.35", | ||||
|     "dui": "^2.0.0", | ||||
|     "echarts-wordcloud": "^2.0.0", | ||||
|     "hash.js": "^1.1.7", | ||||
|     "html2canvas": "^1.4.1", | ||||
| @@ -41,7 +31,9 @@ | ||||
|     "serialize-javascript": "^6.0.0", | ||||
|     "sortablejs": "^1.12.0", | ||||
|     "vue-carousel": "^0.18.0", | ||||
|     "vue-cropper": "^0.6.5", | ||||
|     "vue-draggable-resizable": "^2.3.0", | ||||
|     "vue-qr": "^4.0.9", | ||||
|     "vue-ruler-tool": "^1.2.4", | ||||
|     "vue-style-loader": "^4.1.3", | ||||
|     "vuedraggable": "^2.24.3" | ||||
| @@ -63,6 +55,7 @@ | ||||
|     "inquirer": "^6.5.2", | ||||
|     "mockjs": "^1.1.0", | ||||
|     "node-ipc": "^9.2.1", | ||||
|     "nuxt": "^2.18.1", | ||||
|     "readline": "^1.3.0", | ||||
|     "sass": "~1.32.6", | ||||
|     "sass-loader": "^7.3.1", | ||||
| @@ -73,14 +66,6 @@ | ||||
|     "vuex": "^3.5.1", | ||||
|     "vuex-persistedstate": "^2.7.1" | ||||
|   }, | ||||
|   "vetur": { | ||||
|     "attributes": "./attributes.json" | ||||
|   }, | ||||
|   "postcss": { | ||||
|     "plugins": { | ||||
|       "autoprefixer": {} | ||||
|     } | ||||
|   }, | ||||
|   "browserslist": [ | ||||
|     "> 1%", | ||||
|     "last 2 versions", | ||||
|   | ||||
| @@ -1,62 +0,0 @@ | ||||
| <template> | ||||
|   <div class="AppHealthReport"> | ||||
|     <keep-alive :include="['List']"> | ||||
|       <component ref="component" :is="component" @change="onChange" :params="params" :instance="instance" :dict="dict"></component> | ||||
|     </keep-alive> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import List from './components/List.vue' | ||||
|   import Detail from './components/Detail.vue' | ||||
|  | ||||
|   export default { | ||||
|     name: 'AppHealthReport', | ||||
|     label: '健康上报', | ||||
|  | ||||
|     components: { | ||||
|       List, | ||||
|       Detail | ||||
|     }, | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object, | ||||
|       permissions: Function | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       return { | ||||
|         component: 'List', | ||||
|         params: {} | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       onChange (data) { | ||||
|         if (data.type === 'Detail') { | ||||
|           this.component = 'Detail' | ||||
|           this.isShowDetail = true | ||||
|           this.params = data.params | ||||
|         } | ||||
|  | ||||
|         if (data.type === 'list') { | ||||
|           this.component = 'List' | ||||
|           this.params = data.params | ||||
|  | ||||
|           this.$nextTick(() => { | ||||
|             if (data.isRefresh) { | ||||
|               this.$refs.component.getList() | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .AppHealthReport { | ||||
|     height: 100%; | ||||
|   } | ||||
| </style> | ||||
| @@ -1,316 +0,0 @@ | ||||
|  <template> | ||||
|   <ai-detail isHasSidebar> | ||||
|     <template slot="title"> | ||||
|       <ai-title title="详情" isShowBack isShowBottomBorder @onBackClick="cancel(true)"> | ||||
|       </ai-title> | ||||
|     </template> | ||||
|     <template slot="content"> | ||||
|       <AiSidebar :tabTitle="tabList" v-model="currIndex"></AiSidebar> | ||||
|       <div v-show="currIndex === 0"> | ||||
|         <ai-card title="基本信息" v-show="currIndex === 0"> | ||||
|           <template #content> | ||||
|             <ai-wrapper | ||||
|               label-width="120px"> | ||||
|               <ai-info-item label="姓名" :value="info.name"></ai-info-item> | ||||
|               <ai-info-item label="上报时间" :value="info.createTime"></ai-info-item> | ||||
|               <ai-info-item label="身份证号" :value="info.idNumber"></ai-info-item> | ||||
|               <ai-info-item label="所属地区" :value="info.areaName"></ai-info-item> | ||||
|               <ai-info-item label="详细地址" isLine :value="info.address"></ai-info-item> | ||||
|             </ai-wrapper> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|       </div> | ||||
|       <ai-card title="每日上报" v-show="currIndex === 1"> | ||||
|         <template #content> | ||||
|           <ai-table | ||||
|             class="detail-table__table" | ||||
|             :tableData="tableData" | ||||
|             :col-configs="colConfigs" | ||||
|             :total="total" | ||||
|             :current.sync="search.current" | ||||
|             :size.sync="search.size" | ||||
|             @getList="getList"> | ||||
|             <el-table-column slot="options" width="140px" fixed="right" label="操作" align="center"> | ||||
|               <template slot-scope="{ row }"> | ||||
|                 <div class="table-options"> | ||||
|                   <el-button type="text" @click="toDetail(row.id)">详情</el-button> | ||||
|                 </div> | ||||
|               </template> | ||||
|             </el-table-column> | ||||
|           </ai-table> | ||||
|           <ai-dialog | ||||
|             :visible.sync="isShow" | ||||
|             width="890px" | ||||
|             customFooter | ||||
|             title="上报详情"> | ||||
|             <ai-bar title="健康状况"></ai-bar> | ||||
|             <ai-wrapper | ||||
|               label-width="120px"> | ||||
|               <ai-info-item label="当前体温"> | ||||
|                 <span :style="{color: reportInfo.temperature > 37.3 ? '#FF4466' : '#42D784'}">{{ reportInfo.temperature }}℃</span> | ||||
|               </ai-info-item> | ||||
|               <ai-info-item label="14天内是否接触新冠确诊或疑似患者"> | ||||
|                 <span :style="{color: reportInfo.touchInFourteen === '0' ? '#42D784' : '#FF4466'}">{{ $dict.getLabel('epidemicTouchInFourteen', reportInfo.touchInFourteen) }}</span> | ||||
|               </ai-info-item> | ||||
|               <ai-info-item label="当前健康状况" isLine> | ||||
|                 <span :style="{color: !reportInfo.isHealth ? '#42D784' : '#FF4466'}">{{ reportInfo.healthName }}</span> | ||||
|               </ai-info-item> | ||||
|             </ai-wrapper> | ||||
|             <ai-bar title="核酸检测信息"></ai-bar> | ||||
|             <ai-wrapper | ||||
|               label-width="120px"> | ||||
|               <ai-info-item label="检测日期"> | ||||
|                 <span>{{ reportInfo.checkTime && reportInfo.checkTime.split(' ')[0] }}</span> | ||||
|               </ai-info-item> | ||||
|               <ai-info-item label="检测结果"> | ||||
|                 <span :style="{color: reportInfo.checkResult === '0' ? '#42D784' : '#FF4466'}">{{ $dict.getLabel('epidemicRecentTestResult', reportInfo.checkResult) }}</span> | ||||
|               </ai-info-item> | ||||
|               <ai-info-item label="健康码状态"> | ||||
|                 <span :style="{color: (reportInfo.healthCode === '0' || reportInfo.healthCode === '1') ? '#42D784' : '#FF4466'}">{{ $dict.getLabel('epidemicHealthCode', reportInfo.healthCode) }}</span> | ||||
|               </ai-info-item> | ||||
|               <ai-info-item label="已接种疫苗次数"> | ||||
|                 <span>{{ $dict.getLabel('epidemicVaccineTime', reportInfo.vaccine) }}</span> | ||||
|               </ai-info-item> | ||||
|               <ai-info-item label="本人健康码截图" isLine> | ||||
|                 <ai-uploader | ||||
|                   :instance="instance" | ||||
|                   v-model="reportInfo.checkPhoto" | ||||
|                   disabled | ||||
|                   :limit="9"> | ||||
|                 </ai-uploader> | ||||
|               </ai-info-item> | ||||
|             </ai-wrapper> | ||||
|             <div class="dialog-footer" slot="footer"> | ||||
|               <el-button @click="isShow = false">关闭</el-button> | ||||
|             </div> | ||||
|           </ai-dialog> | ||||
|         </template> | ||||
|       </ai-card> | ||||
|       <div v-show="currIndex === 2"> | ||||
|         <ai-card title="异常处理"> | ||||
|           <template #right> | ||||
|             <el-button type="primary" v-if="info.status === '0'" @click="release">解除异常</el-button> | ||||
|           </template> | ||||
|           <template #content> | ||||
|             <ai-wrapper | ||||
|               label-width="120px"> | ||||
|               <ai-info-item label="姓名" :value="info.name"></ai-info-item> | ||||
|               <ai-info-item label="填报时间" :value="info.createTime"></ai-info-item> | ||||
|               <ai-info-item label="身份证号" :value="info.idNumber"></ai-info-item> | ||||
|               <ai-info-item label="手机号码" :value="info.phone"></ai-info-item> | ||||
|               <ai-info-item label="异常状况" isLine> | ||||
|                 <span :style="{color: info.unusual ? 'red' : '#333'}">{{ info.unusual || '-' }}</span> | ||||
|               </ai-info-item> | ||||
|               <ai-info-item label="异常解除人" v-if="info.releaseName && info.status === '1'" :value="info.releaseName"></ai-info-item> | ||||
|               <ai-info-item label="异常解除时间" v-if="info.releaseTime && info.status === '1'" :value="info.releaseTime"></ai-info-item> | ||||
|             </ai-wrapper> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|         <ai-card title="异常情况记录"> | ||||
|           <template #right> | ||||
|             <el-button type="primary" v-if="info.status === '0'" @click="isShowAdd = true">添加记录</el-button> | ||||
|           </template> | ||||
|           <template #content> | ||||
|             <ai-table | ||||
|               :tableData="recordList" | ||||
|               :col-configs="recordConfigs" | ||||
|               :total="recordTotal" | ||||
|               :current.sync="recordSerch.current" | ||||
|               :size.sync="recordSerch.size" | ||||
|               @getList="getRecordList"> | ||||
|               <el-table-column slot="options" width="120px" fixed="right" label="操作" align="center"> | ||||
|                 <template slot-scope="{ row }"> | ||||
|                   <div class="table-options"> | ||||
|                     <el-button type="text" @click="remove(row.id)">删除</el-button> | ||||
|                   </div> | ||||
|                 </template> | ||||
|               </el-table-column> | ||||
|             </ai-table> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|         <ai-dialog | ||||
|           :visible.sync="isShowAdd" | ||||
|           width="800px" | ||||
|           @close="form.content = ''" | ||||
|           title="添加异常记录" | ||||
|           @onConfirm="onConfirm"> | ||||
|           <el-form class="ai-form" label-width="120px" :model="form" ref="form"> | ||||
|             <el-form-item label="异常记录" prop="content" style="width: 100%;" :rules="[{ required: true, message: '请输入异常记录' }]"> | ||||
|               <el-input type="textarea" :rows="5" :maxlength="500" v-model="form.content" clearable placeholder="请输入异常记录" show-word-limit></el-input> | ||||
|             </el-form-item> | ||||
|           </el-form> | ||||
|         </ai-dialog> | ||||
|       </div> | ||||
|     </template> | ||||
|   </ai-detail> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   export default { | ||||
|     name: 'Detail', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object, | ||||
|       params: Object | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       return { | ||||
|         total: 0, | ||||
|         info: {}, | ||||
|         id: '', | ||||
|         isShowAdd: false, | ||||
|         recordTotal: 0, | ||||
|         recordSerch: { | ||||
|           current: 1, | ||||
|           size: 10 | ||||
|         }, | ||||
|         search: { | ||||
|           current: 1, | ||||
|           size: 10 | ||||
|         }, | ||||
|         form: { | ||||
|           content: '' | ||||
|         }, | ||||
|         recordConfigs: [ | ||||
|           {prop: 'content', label: '异常记录', align: 'center' }, | ||||
|           {prop: 'createTime', label: '创建时间', align: 'center'}, | ||||
|           {prop: 'createUserName', label: '记录人', align: 'center' } | ||||
|         ], | ||||
|         reportInfo: {}, | ||||
|         isShow: false, | ||||
|         currIndex: 0, | ||||
|         tableData: [], | ||||
|         recordList: [], | ||||
|         colConfigs: [ | ||||
|           {prop: 'createTime', label: '上报日期', align: 'center', dateFormat: 'YYYY-MM-DD'}, | ||||
|           {prop: 'status', label: '健康状态', align: 'center', format: v => v === '0' ? '异常' : '正常' } | ||||
|         ], | ||||
|         tabList: ['基本信息', '每日上报', '异常处理'] | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     created () { | ||||
|       if (this.params && this.params.id) { | ||||
|         this.id = this.params.id | ||||
|         this.dict.load(['epidemicRecentHealth', 'epidemicRecentTravel', 'epidemicTouchInFourteen', 'epidemicMemberType', 'epidemicRecentTestResult']).then(() => { | ||||
|           this.getInfo(this.params.id) | ||||
|           this.getList() | ||||
|           this.getRecordList() | ||||
|         }) | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       getInfo (id) { | ||||
|         this.instance.post(`/app/appepidemicreportmember/queryDetailById?id=${id}`).then(res => { | ||||
|           if (res.code === 0) { | ||||
|             this.info = res.data | ||||
|             this.currIndex = 0 | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       getRecordList () { | ||||
|         this.instance.post(`/app/appepidemicunusuallog/list`, null, { | ||||
|           params: { | ||||
|             ...this.search, | ||||
|             recordId: this.params.id | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.recordList = res.data.records | ||||
|             this.recordTotal = res.data.total | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       onConfirm() { | ||||
|         this.$refs.form.validate(v => { | ||||
|           if (v) { | ||||
|             this.instance.post('/app/appepidemicunusuallog/addOrUpdate', { | ||||
|               ...this.form, | ||||
|               recordId: this.params.id | ||||
|             }).then(res => { | ||||
|               if (res?.code == 0) { | ||||
|                 this.isShowAdd = false | ||||
|                 this.getRecordList(this.params.id) | ||||
|                 this.$message.success('添加成功!') | ||||
|               } | ||||
|             }) | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       release () { | ||||
|         this.$confirm('确定解除异常?').then(() => { | ||||
|           this.instance.post(`/app/appepidemicreportmember/release`, { | ||||
|             id: this.params.id | ||||
|           }).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               this.$message.success('解除异常成功!') | ||||
|               this.currIndex = 0 | ||||
|               this.getInfo(this.params.id) | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       remove(id) { | ||||
|         this.$confirm('确定删除该数据?').then(() => { | ||||
|           this.instance.post(`/app/appepidemicunusuallog/delete?ids=${id}`).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               this.$message.success('删除成功!') | ||||
|               this.getRecordList(this.params.id) | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       toDetail (id) { | ||||
|         this.instance.post(`/app/appepidemichealthreport/queryDetailById?id=${id}`).then(res => { | ||||
|           if (res.code === 0) { | ||||
|             this.reportInfo = res.data | ||||
|             this.reportInfo.checkPhoto = JSON.parse(res.data.checkPhoto) | ||||
|             let healthName = '' | ||||
|             this.reportInfo.isHealth = false | ||||
|             res.data.health.split(',').forEach(v => { | ||||
|               if (v > 0) { | ||||
|                 this.reportInfo.isHealth = true | ||||
|               } | ||||
|               healthName = healthName + this.dict.getLabel('epidemicRecentHealth', v) | ||||
|             }) | ||||
|             this.reportInfo.healthName = healthName | ||||
|  | ||||
|             this.isShow = true | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       getList () { | ||||
|         this.instance.post(`/app/appepidemichealthreport/list`, null, { | ||||
|           params: { | ||||
|             ...this.search, | ||||
|             memberId: this.params.id | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.tableData = res.data.records | ||||
|             this.total = res.data.total | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       cancel (isRefresh) { | ||||
|         this.$emit('change', { | ||||
|           type: 'list', | ||||
|           isRefresh: !!isRefresh | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| </style> | ||||
| @@ -1,293 +0,0 @@ | ||||
| <template> | ||||
|   <ai-list class="list"> | ||||
|     <ai-title slot="title" title="健康上报" v-if="search.areaId" isShowBottomBorder :instance="instance" :disabledLevel="disabledLevel" isShowArea v-model="search.areaId" @change="changeArea"></ai-title> | ||||
|     <template slot="content"> | ||||
|       <div class="statistics-top"> | ||||
|         <div class="statistics-top__item"> | ||||
|           <span>上报人数</span> | ||||
|           <h2 style="color: #2266FF;">{{ info.total }}</h2> | ||||
|         </div> | ||||
|         <div class="statistics-top__item"> | ||||
|           <span>今日上报</span> | ||||
|           <h2 style="color: #22AA99;">{{ info.today }}</h2> | ||||
|         </div> | ||||
|         <div class="statistics-top__item"> | ||||
|           <span>今日未报</span> | ||||
|           <h2 style="color: #F8B425">{{ info.unReport }}</h2> | ||||
|         </div> | ||||
|         <div class="statistics-top__item"> | ||||
|           <span>今日异常</span> | ||||
|           <h2 style="color: red">{{ info.unusual }}</h2> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="content"> | ||||
|         <ai-search-bar bottomBorder> | ||||
|           <template #left> | ||||
|             <ai-select | ||||
|               v-model="search.checkResult" | ||||
|               clearable | ||||
|               placeholder="请选择检查结果" | ||||
|               :selectList="dict.getDict('epidemicRecentTestResult')" | ||||
|               @change="search.current = 1, getList()"> | ||||
|             </ai-select> | ||||
|             <ai-select | ||||
|               v-model="search.today" | ||||
|               clearable | ||||
|               placeholder="今日是否上报" | ||||
|               :selectList="dictList" | ||||
|               @change="search.current = 1, getList()"> | ||||
|             </ai-select> | ||||
|             <ai-download :instance="instance" url="/app/appepidemicreportmember/export" :params="search" fileName="健康上报" :disabled="tableData.length == 0"> | ||||
|               <el-button icon="iconfont iconExported" :disabled="tableData.length == 0">导出</el-button> | ||||
|             </ai-download> | ||||
|           </template> | ||||
|           <template #right> | ||||
|             <el-input | ||||
|               v-model="search.name" | ||||
|               size="small" | ||||
|               placeholder="请输入姓名" | ||||
|               clearable | ||||
|               v-throttle="() => {search.current = 1, getList()}" | ||||
|               @clear="search.current = 1, search.name = '', getList()" | ||||
|               suffix-icon="iconfont iconSearch"> | ||||
|             </el-input> | ||||
|           </template> | ||||
|         </ai-search-bar> | ||||
|         <ai-table | ||||
|           :tableData="tableData" | ||||
|           :col-configs="colConfigs" | ||||
|           :total="total" | ||||
|           v-loading="loading" | ||||
|           style="margin-top: 16px;" | ||||
|           :current.sync="search.current" | ||||
|           :size.sync="search.size" | ||||
|           @getList="getList"> | ||||
|           <el-table-column slot="healthCode" align="center" label="健康码"> | ||||
|             <template slot-scope="{ row }"> | ||||
|               <ai-uploader | ||||
|                 v-if="row.healthCode" | ||||
|                 :instance="instance" | ||||
|                 :value="JSON.parse(row.healthCode)" | ||||
|                 disabled | ||||
|                 :limit="1"> | ||||
|               </ai-uploader> | ||||
|             </template> | ||||
|           </el-table-column> | ||||
|           <el-table-column slot="options" width="140px" fixed="right" label="操作" align="center"> | ||||
|             <template slot-scope="{ row }"> | ||||
|               <div class="table-options"> | ||||
|                 <el-button type="text" @click="toDetail(row.id)">详情</el-button> | ||||
|                 <el-button type="text" @click="remove(row.id)">删除</el-button> | ||||
|               </div> | ||||
|             </template> | ||||
|           </el-table-column> | ||||
|         </ai-table> | ||||
|       </div> | ||||
|     </template> | ||||
|   </ai-list> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import { mapState } from 'vuex' | ||||
|   export default { | ||||
|     name: 'List', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       return { | ||||
|         search: { | ||||
|           current: 1, | ||||
|           size: 10, | ||||
|           name: '', | ||||
|           checkResult: '', | ||||
|           areaId: '', | ||||
|           today: '' | ||||
|         }, | ||||
|         dictList: [{ | ||||
|           dictName: '否', | ||||
|           dictValue: '0' | ||||
|         }, { | ||||
|           dictName: '是', | ||||
|           dictValue: '1' | ||||
|         }], | ||||
|         info: {}, | ||||
|         colConfigs: [ | ||||
|           { prop: 'name', label: '姓名' }, | ||||
|           { prop: 'phone', align: 'center', label: '手机号码' }, | ||||
|           { prop: 'areaName', align: 'center', label: '所属地区', width: '200px' }, | ||||
|           { prop: 'reportTime', align: 'center', label: '上报时间', width: '200px' }, | ||||
|           { | ||||
|             prop: 'vaccine', | ||||
|             align: 'center', | ||||
|             label: '已接种情况', | ||||
|             render: (h, {row}) => { | ||||
|               return h('span', { | ||||
|                 style: { | ||||
|                 } | ||||
|               }, row.today === '0' ? '-' : (row.vaccine || 0 + '次')) | ||||
|             } | ||||
|           }, | ||||
|           { prop: 'healthCode', align: 'center', label: '健康码', format: v => v ? this.dict.getLabel('epidemicHealthCode', v) : '-' }, | ||||
|           { prop: 'checkTime', align: 'center', label: '核酸日期', format: v => v ? v.split(' ')[0] : '-' }, | ||||
|           { prop: 'checkResult', align: 'center', label: '检测结果', format: v => v ? this.dict.getLabel('epidemicRecentTestResult', v) : '-' }, | ||||
|           { | ||||
|             prop: 'status', | ||||
|             align: 'center', | ||||
|             label: '健康状态', | ||||
|             render: (h, {row}) => { | ||||
|               return h('span', { | ||||
|                 style: { | ||||
|                   color: row.status === '0' ? 'red' : '#333' | ||||
|                 } | ||||
|               }, row.today === '0' ? '-' : (row.status === '0' ? '异常' : '正常')) | ||||
|             } | ||||
|           }, | ||||
|           { prop: 'today', align: 'center', label: '今日上报', format: v => v === '0' ? '未上报' : '已上报' }, | ||||
|         ], | ||||
|         tableData: [], | ||||
|         total: 0, | ||||
|         loading: false, | ||||
|         disabledLevel: 0 | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|       ...mapState(['user']), | ||||
|  | ||||
|       param () { | ||||
|         return { | ||||
|  | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     created () { | ||||
|       this.disabledLevel = this.user.info.areaList.length - 1 | ||||
|       this.search.areaId = this.user.info.areaId | ||||
|       this.loading = true | ||||
|       this.dict.load(['epidemicTouchInFourteen', 'epidemicRecentHealth', 'epidemicRecentTestResult', 'epidemicHealthCode', 'epidemicVaccineTime']).then(() => { | ||||
|         this.getList() | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       getList () { | ||||
|         this.instance.post(`/app/appepidemicreportmember/list`, null, { | ||||
|           params: { | ||||
|             ...this.search | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.tableData = res.data.records | ||||
|             this.total = res.data.total | ||||
|             this.loading = false | ||||
|           } else { | ||||
|             this.loading = false | ||||
|           } | ||||
|         }).catch(() => { | ||||
|           this.loading = false | ||||
|         }) | ||||
|  | ||||
|         this.getTotalInfo() | ||||
|       }, | ||||
|  | ||||
|       remove(id) { | ||||
|         this.$confirm('确定删除该数据?').then(() => { | ||||
|           this.instance.post(`/app/appepidemicreportmember/delete?ids=${id}`).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               this.$message.success('删除成功!') | ||||
|               this.getTotalInfo() | ||||
|               this.getList() | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       toDetail (id) { | ||||
|         this.$emit('change', { | ||||
|           type: 'Detail', | ||||
|           params: { | ||||
|             id: id || '' | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       changeArea () { | ||||
|         this.search.current = 1 | ||||
|  | ||||
|         this.$nextTick(() => { | ||||
|           this.getList() | ||||
|           this.getTotalInfo() | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       getTotalInfo () { | ||||
|         this.instance.post(`/app/appepidemicreportmember/statistic`, null, { | ||||
|           params: { | ||||
|             areaId: this.search.areaId | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.info = res.data | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
|   .list { | ||||
|     :deep( .ai-list__content ){ | ||||
|       padding: 0!important; | ||||
|  | ||||
|       .ai-list__content--right-wrapper { | ||||
|         background: transparent!important; | ||||
|         box-shadow: none!important; | ||||
|         margin: 0!important; | ||||
|         padding: 12px 16px 12px!important; | ||||
|       } | ||||
|     } | ||||
|     .statistics-top { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       margin-bottom: 20px; | ||||
|  | ||||
|       & > div { | ||||
|         flex: 1; | ||||
|         height: 96px; | ||||
|         line-height: 1; | ||||
|         margin-right: 20px; | ||||
|         padding: 16px 24px; | ||||
|         background: #FFFFFF; | ||||
|         box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.15); | ||||
|         border-radius: 4px; | ||||
|  | ||||
|         &:last-child { | ||||
|           margin-right: 0; | ||||
|         } | ||||
|  | ||||
|         h3 { | ||||
|           font-size: 24px; | ||||
|         } | ||||
|  | ||||
|         span { | ||||
|           display: block; | ||||
|           margin-bottom: 16px; | ||||
|           color: #888888; | ||||
|           font-size: 16px; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .content { | ||||
|       padding: 16px; | ||||
|       background: #FFFFFF; | ||||
|       box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.15); | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
| @@ -1,105 +0,0 @@ | ||||
| <template> | ||||
|   <ai-list class="AppReturnHomeRegister" v-if="!isShowDetail"> | ||||
|     <template slot="title"> | ||||
|       <ai-title title="返乡登记" :isShowBottomBorder="false" :fullname.sync="areaName" v-model="areaId" :instance="instance" @change="onAreaChange"></ai-title> | ||||
|     </template> | ||||
|     <template slot="tabs"> | ||||
|       <el-tabs v-model="currIndex"> | ||||
|         <el-tab-pane v-for="(tab,i) in tabs" :key="i" :label="tab.label"> | ||||
|           <component :areaId="areaId" :ref="String(i)" v-if="currIndex == i" :is="tab.comp" @change="onChange" lazy :instance="instance" :dict="dict" :permissions="permissions"/> | ||||
|         </el-tab-pane> | ||||
|       </el-tabs> | ||||
|     </template> | ||||
|   </ai-list> | ||||
|   <Detail v-else-if="component === 'Detail'" :params="params" :instance="instance" :dict="dict" :permissions="permissions" @change="onChange"></Detail> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import List from './components/List.vue' | ||||
|   import Detail from './components/Detail.vue' | ||||
|   import AbnormalList from './components/AbnormalList.vue' | ||||
|   import { mapState } from 'vuex' | ||||
|  | ||||
|   export default { | ||||
|     name: 'AppReturnHomeRegister', | ||||
|     label: '返乡登记', | ||||
|  | ||||
|     components: { | ||||
|       List, | ||||
|       Detail, | ||||
|       AbnormalList | ||||
|     }, | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object, | ||||
|       permissions: Function | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|       ...mapState(['user']), | ||||
|  | ||||
|       tabs () { | ||||
|         const tabList = [ | ||||
|           {label: '返乡登记', name: 'List', comp: List, permission: ''}, | ||||
|           // {label: '历史异常人员', name: 'AbnormalList', comp: AbnormalList, permission: ''} | ||||
|         ] | ||||
|  | ||||
|         return tabList | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       return { | ||||
|         activeName: 'JoinEvent', | ||||
|         currIndex: '0', | ||||
|         component: 'List', | ||||
|         params: {}, | ||||
|         areaId: '', | ||||
|         isShowDetail: false, | ||||
|         areaName: '' | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     created () { | ||||
|       this.areaId = this.user.info.areaId | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       onChange (data) { | ||||
|         if (data.type === 'Detail') { | ||||
|           this.component = 'Detail' | ||||
|           this.isShowDetail = true | ||||
|           this.params = data.params | ||||
|         } | ||||
|         if (data.type === 'AbnormalList') { | ||||
|           this.component = 'AbnormalList' | ||||
|           this.isShowDetail = false | ||||
|           this.params = data.params | ||||
|         } | ||||
|  | ||||
|         if (data.type === 'List') { | ||||
|           this.component = 'List' | ||||
|           this.isShowDetail = false | ||||
|           this.params = data.params | ||||
|  | ||||
|           this.$nextTick(() => { | ||||
|             if (data.isRefresh) { | ||||
|               this.$refs.component.getList() | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }, | ||||
|  | ||||
|       onAreaChange () { | ||||
|         this.$refs[this.currIndex][0].changeArea() | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .AppReturnHomeRegister { | ||||
|     height: 100%; | ||||
|   } | ||||
| </style> | ||||
| @@ -1,200 +0,0 @@ | ||||
| <template> | ||||
|   <ai-list isTabs> | ||||
|     <template slot="content"> | ||||
|       <ai-search-bar bottomBorder> | ||||
|         <template #left> | ||||
|           <ai-select | ||||
|             v-model="search.status" | ||||
|             clearable | ||||
|             placeholder="请选择健康状态" | ||||
|             :selectList="dictList" | ||||
|             @change="search.current = 1, getList()"> | ||||
|           </ai-select> | ||||
|           <ai-download :instance="instance" url="/app/appepidemicbackhomerecord/export" :params="param" fileName="返乡登记" :disabled="tableData.length == 0"> | ||||
|             <el-button icon="iconfont iconExported" :disabled="tableData.length == 0">导出</el-button> | ||||
|           </ai-download> | ||||
|         </template> | ||||
|         <template #right> | ||||
|           <el-input | ||||
|             v-model="search.name" | ||||
|             size="small" | ||||
|             placeholder="请输入姓名" | ||||
|             clearable | ||||
|             v-throttle="() => {search.current = 1, getList()}" | ||||
|             @clear="search.current = 1, search.name = '', getList()" | ||||
|             suffix-icon="iconfont iconSearch"> | ||||
|           </el-input> | ||||
|         </template> | ||||
|       </ai-search-bar> | ||||
|       <ai-table | ||||
|         :tableData="tableData" | ||||
|         :col-configs="colConfigs" | ||||
|         :total="total" | ||||
|         v-loading="loading" | ||||
|         style="margin-top: 16px;" | ||||
|         :current.sync="search.current" | ||||
|         :size.sync="search.size" | ||||
|         @getList="getList"> | ||||
|         <el-table-column slot="options" width="140px" fixed="right" label="操作" align="center"> | ||||
|           <template slot-scope="{ row }"> | ||||
|             <div class="table-options"> | ||||
|               <el-button type="text" @click="toDetail(row.id)">详情</el-button> | ||||
|               <el-button type="text" @click="remove(row.id)">删除</el-button> | ||||
|             </div> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|       </ai-table> | ||||
|     </template> | ||||
|   </ai-list> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import { mapState } from 'vuex' | ||||
|   export default { | ||||
|     name: 'AbnormalList', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       return { | ||||
|         search: { | ||||
|           current: 1, | ||||
|           size: 10, | ||||
|           name: '', | ||||
|           arriveAreaId: '', | ||||
|           status: '' | ||||
|         }, | ||||
|         dictList: [{ | ||||
|           dictName: '异常', | ||||
|           dictValue: '0' | ||||
|         }, { | ||||
|           dictName: '正常', | ||||
|           dictValue: '1' | ||||
|         }], | ||||
|         info: {}, | ||||
|         colConfigs: [ | ||||
|           { prop: 'name', label: '姓名' }, | ||||
|           { prop: 'phone', align: 'center', label: '手机号码' }, | ||||
|           { prop: 'startTime', align: 'center', label: '出发时间', format: v => v.substr(0, v.length - 3) }, | ||||
|           { | ||||
|             prop: 'startAreaName', | ||||
|             align: 'center', | ||||
|             label: '出发地区' | ||||
|           }, | ||||
|           { prop: 'arriveTime', align: 'center', label: '到达时间', format: v => v.substr(0, v.length - 3) }, | ||||
|           { | ||||
|             prop: 'arriveAreaName', | ||||
|             align: 'center', | ||||
|             label: '到达地区' | ||||
|           }, | ||||
|           { prop: 'checkTime', align: 'center', label: '核酸日期', format: v => v.split(' ')[0] }, | ||||
|           { | ||||
|             prop: 'status', | ||||
|             align: 'center', | ||||
|             label: '健康状态', | ||||
|             render: (h, {row}) => { | ||||
|               return h('span', { | ||||
|                 style: { | ||||
|                   color: row.status === '0' ? 'red' : '#333' | ||||
|                 } | ||||
|               }, row.status === '0' ? '异常' : '正常') | ||||
|             } | ||||
|           } | ||||
|         ], | ||||
|         ids: [], | ||||
|         tableData: [], | ||||
|         total: 0, | ||||
|         loading: false, | ||||
|         disabledLevel: 0 | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|       ...mapState(['user']), | ||||
|  | ||||
|       param () { | ||||
|         return this.search | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     created () { | ||||
|       this.disabledLevel = this.user.info.areaList.length - 1 | ||||
|       this.search.arriveAreaId = this.user.info.areaId | ||||
|       this.loading = true | ||||
|       this.dict.load(['marriageType', 'marriagePersonType', 'modeType']).then(() => { | ||||
|         this.getList() | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       getList () { | ||||
|         this.instance.post(`/app/appepidemicbackhomerecord/list`, null, { | ||||
|           params: { | ||||
|             ...this.search | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.tableData = res.data.records | ||||
|             this.total = res.data.total | ||||
|             this.loading = false | ||||
|           } else { | ||||
|             this.loading = false | ||||
|           } | ||||
|         }).catch(() => { | ||||
|           this.loading = false | ||||
|         }) | ||||
|  | ||||
|         this.getTotalInfo() | ||||
|       }, | ||||
|  | ||||
|       toDetail (id) { | ||||
|         this.$emit('change', { | ||||
|           type: 'Detail', | ||||
|           params: { | ||||
|             id: id || '' | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       changeArea () { | ||||
|         this.search.current = 1 | ||||
|  | ||||
|         this.$nextTick(() => { | ||||
|           this.getList() | ||||
|           this.getTotalInfo() | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       remove(id) { | ||||
|         this.$confirm('确定删除该数据?').then(() => { | ||||
|           this.instance.post(`/app/appepidemicbackhomerecord/delete?ids=${id}`).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               this.$message.success('删除成功!') | ||||
|               this.getTotalInfo() | ||||
|               this.getList() | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|  | ||||
|       getTotalInfo () { | ||||
|         this.instance.post(`/app/appepidemicbackhomerecord/statistic`, null, { | ||||
|           params: { | ||||
|             areaId: this.search.arriveAreaId | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.info = res.data | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| </style> | ||||
| @@ -1,279 +0,0 @@ | ||||
|  <template> | ||||
|   <ai-detail isHasSidebar> | ||||
|     <template slot="title"> | ||||
|       <ai-title title="返乡登记详情" isShowBack isShowBottomBorder @onBackClick="cancel(false)"> | ||||
|       </ai-title> | ||||
|     </template> | ||||
|     <template slot="content"> | ||||
|       <AiSidebar :tabTitle="tabList" v-model="currIndex"></AiSidebar> | ||||
|       <div v-show="currIndex === 0"> | ||||
|         <ai-card title="基本信息" v-show="currIndex === 0"> | ||||
|           <template #content> | ||||
|             <ai-wrapper | ||||
|               label-width="120px"> | ||||
|               <ai-info-item label="姓名" :value="info.name"></ai-info-item> | ||||
|               <ai-info-item label="填报时间" :value="info.createTime"></ai-info-item> | ||||
|               <ai-info-item label="身份证号" :value="info.idNumber"></ai-info-item> | ||||
|               <ai-info-item label="手机号码" :value="info.phone"></ai-info-item> | ||||
|               <ai-info-item label="人员类别" isLine> | ||||
|                 <span :style="(info.type == 0 || info.type == 3 || info.type ==6 || info.type == 9)? 'color:#42D784;' : 'color:#f46;'">{{dict.getLabel('epidemicRecentPersonType', info.type)}}</span> | ||||
|               </ai-info-item> | ||||
|             </ai-wrapper> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|         <ai-card title="行程信息"> | ||||
|           <template #content> | ||||
|             <ai-wrapper | ||||
|               label-width="120px"> | ||||
|               <ai-info-item label="出发时间" :value="info.startTime"></ai-info-item> | ||||
|               <ai-info-item label="出发地区" > | ||||
|                 <span  :style="{color: info.denger == 1 ? '#FF4466' : '#333'}">{{info.startAreaName}} </span> | ||||
|               </ai-info-item> | ||||
|               <ai-info-item label="出发地址" isLine :value="info.startAddress"></ai-info-item> | ||||
|               <ai-info-item label="出行方式" :value="dict.getLabel('epidemicRecentTravel', info.travelType)"></ai-info-item> | ||||
|               <ai-info-item label="行程描述" isLine :value="info.description"></ai-info-item> | ||||
|               <ai-info-item label="到达时间" :value="info.arriveTime"></ai-info-item> | ||||
|               <ai-info-item label="到达地区" :value="info.arriveAreaName"></ai-info-item> | ||||
|               <ai-info-item label="返乡地址" isLine :value="info.arriveAddress"></ai-info-item> | ||||
|             </ai-wrapper> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|         <ai-card title="健康状况"> | ||||
|           <template #content> | ||||
|             <ai-wrapper | ||||
|               label-width="120px"> | ||||
|               <ai-info-item label="当前体温"> | ||||
|                 <span :style="info.temperature >= 37.3 ? 'color:#f46;' : ''">{{ info.temperature + '℃' }}</span> | ||||
|               </ai-info-item> | ||||
|               <ai-info-item label="14天内是否接触新冠确诊或疑似患者"> | ||||
|                 <span :class="'color-'+info.touchInFourteen">{{$dict.getLabel('epidemicTouchInFourteen', info.touchInFourteen)}}</span>  | ||||
|               </ai-info-item> | ||||
|               <ai-info-item label="当前健康状况">  | ||||
|                 <span></span> | ||||
|                 <span v-for="(item, index) in info.health" :key="index" :style="item != 0 ? 'color:#FF4466;' : ''"><span v-if="index>0">;</span>{{$dict.getLabel('epidemicRecentHealth', item)}}</span> | ||||
|               </ai-info-item> | ||||
|             </ai-wrapper> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|         <ai-card title="核酸检测"> | ||||
|           <template #content> | ||||
|             <ai-wrapper | ||||
|               label-width="120px"> | ||||
|               <ai-info-item label="检测日期" :value="info.checkTime && info.checkTime.split(' ')[0]"></ai-info-item> | ||||
|               <ai-info-item label="检测结果"> | ||||
|                 <span :style="info.checkResult == 1 ? 'color:#f46;' : 'color:#42D784;'">{{$dict.getLabel('epidemicRecentTestResult', info.checkResult)}}</span> | ||||
|               </ai-info-item> | ||||
|               <ai-info-item label="本人健康码截图" isLine> | ||||
|                 <ai-uploader | ||||
|                   :instance="instance" | ||||
|                   v-model="info.checkPhoto" | ||||
|                   disabled | ||||
|                   :limit="9"> | ||||
|                 </ai-uploader> | ||||
|               </ai-info-item> | ||||
|             </ai-wrapper> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|       </div> | ||||
|       <div v-show="currIndex === 1"> | ||||
|         <ai-card title="异常处理"> | ||||
|           <template #right> | ||||
|             <el-button type="primary" v-if="info.status === '0'" @click="release">解除异常</el-button> | ||||
|           </template> | ||||
|           <template #content> | ||||
|             <ai-wrapper | ||||
|               label-width="120px"> | ||||
|               <ai-info-item label="姓名" :value="info.name"></ai-info-item> | ||||
|               <ai-info-item label="填报时间" :value="info.createTime"></ai-info-item> | ||||
|               <ai-info-item label="身份证号" :value="info.idNumber"></ai-info-item> | ||||
|               <ai-info-item label="手机号码" :value="info.phone"></ai-info-item> | ||||
|               <ai-info-item label="人员类别" isLine :value="dict.getLabel('epidemicRecentPersonType', info.type)"></ai-info-item> | ||||
|               <ai-info-item label="异常状况" isLine> | ||||
|                 <span :style="{color: info.unusual ? 'red' : '#333'}">{{ info.unusual || '-' }}</span> | ||||
|               </ai-info-item> | ||||
|               <ai-info-item label="异常解除人" v-if="info.releaseName && info.status === '1'" :value="info.releaseName"></ai-info-item> | ||||
|               <ai-info-item label="异常解除时间" v-if="info.releaseTime && info.status === '1'" :value="info.releaseTime"></ai-info-item> | ||||
|             </ai-wrapper> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|         <ai-card title="异常情况记录"> | ||||
|           <template #right> | ||||
|             <el-button type="primary" v-if="info.status === '0'" @click="isShow = true">添加记录</el-button> | ||||
|           </template> | ||||
|           <template #content> | ||||
|             <ai-table | ||||
|               :tableData="tableData" | ||||
|               :col-configs="colConfigs" | ||||
|               :total="total" | ||||
|               :current.sync="search.current" | ||||
|               :size.sync="search.size" | ||||
|               @getList="getList"> | ||||
|               <el-table-column slot="options" width="120px" fixed="right" label="操作" align="center"> | ||||
|                 <template slot-scope="{ row }"> | ||||
|                   <div class="table-options"> | ||||
|                     <el-button type="text" @click="remove(row.id)">删除</el-button> | ||||
|                   </div> | ||||
|                 </template> | ||||
|               </el-table-column> | ||||
|             </ai-table> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|         <ai-dialog | ||||
|           :visible.sync="isShow" | ||||
|           width="800px" | ||||
|           @close="form.content = ''" | ||||
|           title="添加异常记录" | ||||
|           @onConfirm="onConfirm"> | ||||
|           <el-form class="ai-form" label-width="120px" :model="form" ref="form"> | ||||
|             <el-form-item label="异常记录" prop="content" style="width: 100%;" :rules="[{ required: true, message: '请输入异常记录' }]"> | ||||
|               <el-input type="textarea" :rows="5" :maxlength="500" v-model="form.content" clearable placeholder="请输入异常记录" show-word-limit></el-input> | ||||
|             </el-form-item> | ||||
|           </el-form> | ||||
|         </ai-dialog> | ||||
|       </div> | ||||
|     </template> | ||||
|   </ai-detail> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   export default { | ||||
|     name: 'Detail', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object, | ||||
|       params: Object | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       return { | ||||
|         total: 0, | ||||
|         info: {}, | ||||
|         id: '', | ||||
|         search: { | ||||
|           current: 1, | ||||
|           size: 10 | ||||
|         }, | ||||
|         form: { | ||||
|           content: '' | ||||
|         }, | ||||
|         isShow: false, | ||||
|         currIndex: 0, | ||||
|         tableData: [], | ||||
|         colConfigs: [ | ||||
|           {prop: 'content', label: '异常记录', align: 'center' }, | ||||
|           {prop: 'createTime', label: '创建时间', align: 'center'}, | ||||
|           {prop: 'createUserName', label: '记录人', align: 'center' } | ||||
|         ], | ||||
|         tabList: ['基本信息', '异常处理'] | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     created () { | ||||
|       if (this.params && this.params.id) { | ||||
|         this.id = this.params.id | ||||
|         this.$dict.load(['epidemicRecentHealth', 'epidemicRecentTravel', 'epidemicTouchInFourteen', 'epidemicRecentPersonType', 'epidemicRecentTestResult']).then(() => { | ||||
|           this.getInfo(this.params.id) | ||||
|         }) | ||||
|  | ||||
|         this.getList() | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       getInfo (id) { | ||||
|         this.instance.post(`/app/appepidemicbackhomerecord/queryDetailById?id=${id}`).then(res => { | ||||
|           if (res.code === 0) { | ||||
|             this.info = res.data | ||||
|             this.info.checkPhoto = res.data.checkPhoto ? JSON.parse(res.data.checkPhoto) : [] | ||||
|             let healthName = '' | ||||
|             this.info.isHealth = false | ||||
|             res.data.health.split(',').forEach(v => { | ||||
|               if (v > 0) { | ||||
|                 this.info.isHealth = true | ||||
|               } | ||||
|               healthName = healthName + this.$dict.getLabel('epidemicRecentHealth', v) | ||||
|             }) | ||||
|             this.info.healthName = healthName | ||||
|             this.info.health = this.info.health.split(',') | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       release () { | ||||
|         this.$confirm('确定解除异常?').then(() => { | ||||
|           this.instance.post(`/app/appepidemicbackhomerecord/release?recordId=${this.params.id}`, { | ||||
|             id: this.params.id | ||||
|           }).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               this.$message.success('解除异常成功!') | ||||
|               this.currIndex = 0 | ||||
|               this.getInfo(this.params.id) | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       remove(id) { | ||||
|         this.$confirm('确定删除该数据?').then(() => { | ||||
|           this.instance.post(`/app/appepidemicunusuallog/delete?ids=${id}`).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               this.$message.success('删除成功!') | ||||
|               this.getList() | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       onConfirm() { | ||||
|         this.$refs.form.validate(v => { | ||||
|           if (v) { | ||||
|             this.instance.post('/app/appepidemicunusuallog/addOrUpdate', { | ||||
|               ...this.form, | ||||
|               recordId: this.params.id | ||||
|             }).then(res => { | ||||
|               if (res?.code == 0) { | ||||
|                 this.isShow = false | ||||
|                 this.getList() | ||||
|                 this.$message.success('添加成功!') | ||||
|               } | ||||
|             }) | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       getList () { | ||||
|         this.instance.post(`/app/appepidemicunusuallog/list`, null, { | ||||
|           params: { | ||||
|             ...this.search, | ||||
|             recordId: this.params.id | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.tableData = res.data.records | ||||
|             this.total = res.data.total | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       cancel () { | ||||
|         this.$emit('change', { | ||||
|           type: 'List', | ||||
|           isRefresh: true | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
|   .color-0{ | ||||
|     color: #42D784; | ||||
|   } | ||||
|   .color-1{ | ||||
|     color: #f46; | ||||
|   } | ||||
|   .color-2{ | ||||
|     color: #1365DD; | ||||
|   } | ||||
| </style> | ||||
| @@ -1,273 +0,0 @@ | ||||
| <template> | ||||
|   <ai-list class="list" isTabs> | ||||
|     <template slot="content"> | ||||
|       <div class="statistics-top"> | ||||
|         <div class="statistics-top__item"> | ||||
|           <span>返乡人数</span> | ||||
|           <h2 style="color: #2266FF;">{{ info.total }}</h2> | ||||
|         </div> | ||||
|         <div class="statistics-top__item"> | ||||
|           <span>今日新增返乡</span> | ||||
|           <h2 style="color: #22AA99;">{{ info.today }}</h2> | ||||
|         </div> | ||||
|         <div class="statistics-top__item"> | ||||
|           <span>异常人数</span> | ||||
|           <h2 style="color: #F8B425">{{ info.unusual }}</h2> | ||||
|         </div> | ||||
|         <div class="statistics-top__item"> | ||||
|           <span>今日异常人数</span> | ||||
|           <h2 style="color: red">{{ info.todayUnusual }}</h2> | ||||
|         </div> | ||||
|         <div class="statistics-top__item"> | ||||
|           <span>异常处理</span> | ||||
|           <h2 style="color: red">{{ info.release }}</h2> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="content"> | ||||
|         <ai-search-bar bottomBorder> | ||||
|           <template #left> | ||||
|             <ai-select | ||||
|               v-model="search.status" | ||||
|               clearable | ||||
|               placeholder="请选择健康状态" | ||||
|               :selectList="dictList" | ||||
|               @change="search.current = 1, getList()"> | ||||
|             </ai-select> | ||||
|             <ai-download :instance="instance" url="/app/appepidemicbackhomerecord/export" :params="param" fileName="返乡登记" :disabled="tableData.length == 0"> | ||||
|               <el-button icon="iconfont iconExported" :disabled="tableData.length == 0">导出</el-button> | ||||
|             </ai-download> | ||||
|           </template> | ||||
|           <template #right> | ||||
|             <el-input | ||||
|               v-model="search.name" | ||||
|               size="small" | ||||
|               placeholder="请输入姓名" | ||||
|               clearable | ||||
|               v-throttle="() => {search.current = 1, getList()}" | ||||
|               @clear="search.current = 1, search.name = '', getList()" | ||||
|               suffix-icon="iconfont iconSearch"> | ||||
|             </el-input> | ||||
|           </template> | ||||
|         </ai-search-bar> | ||||
|         <ai-table | ||||
|           :tableData="tableData" | ||||
|           :col-configs="colConfigs" | ||||
|           :total="total" | ||||
|           v-loading="loading" | ||||
|           style="margin-top: 16px;" | ||||
|           :current.sync="search.current" | ||||
|           :size.sync="search.size" | ||||
|           @getList="getList"> | ||||
|           <el-table-column slot="options" width="140px" fixed="right" label="操作" align="center"> | ||||
|             <template slot-scope="{ row }"> | ||||
|               <div class="table-options"> | ||||
|                 <el-button type="text" @click="toDetail(row.id)">详情</el-button> | ||||
|                 <el-button type="text" @click="remove(row.id)">删除</el-button> | ||||
|               </div> | ||||
|             </template> | ||||
|           </el-table-column> | ||||
|         </ai-table> | ||||
|       </div> | ||||
|     </template> | ||||
|   </ai-list> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import { mapState } from 'vuex' | ||||
|   export default { | ||||
|     name: 'List', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object, | ||||
|       areaId: String | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       return { | ||||
|         search: { | ||||
|           current: 1, | ||||
|           size: 10, | ||||
|           name: '', | ||||
|           status: '' | ||||
|         }, | ||||
|         dictList: [{ | ||||
|           dictName: '异常', | ||||
|           dictValue: '0' | ||||
|         }, { | ||||
|           dictName: '正常', | ||||
|           dictValue: '1' | ||||
|         }], | ||||
|         info: {}, | ||||
|         colConfigs: [ | ||||
|           { prop: 'name', label: '姓名' }, | ||||
|           { prop: 'phone', align: 'center', label: '手机号码' }, | ||||
|           { prop: 'startTime', align: 'center', label: '出发时间', format: v => v.substr(0, v.length - 3) }, | ||||
|           { | ||||
|             prop: 'startAreaName', | ||||
|             align: 'center', | ||||
|             label: '出发地区' | ||||
|           }, | ||||
|           { prop: 'arriveTime', align: 'center', label: '到达时间', format: v => v.substr(0, v.length - 3) }, | ||||
|           { | ||||
|             prop: 'arriveAreaName', | ||||
|             align: 'center', | ||||
|             label: '到达地区' | ||||
|           }, | ||||
|           { prop: 'checkTime', align: 'center', label: '核酸日期', format: v => v.split(' ')[0] }, | ||||
|           { | ||||
|             prop: 'status', | ||||
|             align: 'center', | ||||
|             label: '健康状态', | ||||
|             render: (h, {row}) => { | ||||
|               return h('span', { | ||||
|                 style: { | ||||
|                   color: row.status === '0' ? 'red' : '#333' | ||||
|                 } | ||||
|               }, row.status === '0' ? '异常' : '正常') | ||||
|             } | ||||
|           } | ||||
|         ], | ||||
|         ids: [], | ||||
|         tableData: [], | ||||
|         total: 0, | ||||
|         loading: false, | ||||
|         disabledLevel: 0 | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|       ...mapState(['user']), | ||||
|  | ||||
|       param () { | ||||
|         return this.search | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     created () { | ||||
|       this.disabledLevel = this.user.info.areaList.length - 1 | ||||
|       this.loading = true | ||||
|       this.dict.load(['marriageType', 'marriagePersonType', 'modeType']).then(() => { | ||||
|         this.getList() | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       getList () { | ||||
|         this.instance.post(`/app/appepidemicbackhomerecord/list`, null, { | ||||
|           params: { | ||||
|             ...this.search, | ||||
|             arriveAreaId: this.areaId | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.tableData = res.data.records | ||||
|             this.total = res.data.total | ||||
|             this.loading = false | ||||
|           } else { | ||||
|             this.loading = false | ||||
|           } | ||||
|         }).catch(() => { | ||||
|           this.loading = false | ||||
|         }) | ||||
|  | ||||
|         this.getTotalInfo() | ||||
|       }, | ||||
|  | ||||
|       toDetail (id) { | ||||
|         this.$emit('change', { | ||||
|           type: 'Detail', | ||||
|           params: { | ||||
|             id: id || '' | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       changeArea () { | ||||
|         this.search.current = 1 | ||||
|  | ||||
|         this.$nextTick(() => { | ||||
|           this.getList() | ||||
|           this.getTotalInfo() | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       remove(id) { | ||||
|         this.$confirm('确定删除该数据?').then(() => { | ||||
|           this.instance.post(`/app/appepidemicbackhomerecord/delete?ids=${id}`).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               this.$message.success('删除成功!') | ||||
|               this.getTotalInfo() | ||||
|               this.getList() | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|  | ||||
|       getTotalInfo () { | ||||
|         this.instance.post(`/app/appepidemicbackhomerecord/statistic`, null, { | ||||
|           params: { | ||||
|             areaId: this.search.arriveAreaId | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.info = res.data | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
|   .list { | ||||
|     :deep( .ai-list__content ){ | ||||
|       padding: 0!important; | ||||
|  | ||||
|       .ai-list__content--right-wrapper { | ||||
|         background: transparent!important; | ||||
|         box-shadow: none!important; | ||||
|         margin: 0!important; | ||||
|         padding: 0 0 0!important; | ||||
|       } | ||||
|     } | ||||
|     .statistics-top { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       margin-bottom: 20px; | ||||
|  | ||||
|       & > div { | ||||
|         flex: 1; | ||||
|         height: 96px; | ||||
|         line-height: 1; | ||||
|         margin-right: 20px; | ||||
|         padding: 16px 24px; | ||||
|         background: #FFFFFF; | ||||
|         box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.15); | ||||
|         border-radius: 4px; | ||||
|  | ||||
|         &:last-child { | ||||
|           margin-right: 0; | ||||
|         } | ||||
|  | ||||
|         h3 { | ||||
|           font-size: 24px; | ||||
|         } | ||||
|  | ||||
|         span { | ||||
|           display: block; | ||||
|           margin-bottom: 16px; | ||||
|           color: #888888; | ||||
|           font-size: 16px; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .content { | ||||
|       padding: 16px; | ||||
|       background: #FFFFFF; | ||||
|       box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.15); | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
| @@ -1,65 +0,0 @@ | ||||
| <template> | ||||
|   <div class="doc-circulation ailist-wrapper"> | ||||
|     <keep-alive :include="['List']"> | ||||
|       <component ref="component" :moduleName="moduleName" :moduleId="moduleId" :is="component" @change="onChange" :params="params" :instance="instance" :dict="dict"></component> | ||||
|     </keep-alive> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import List from './components/List' | ||||
|   import Add from './components/Add' | ||||
|  | ||||
|   export default { | ||||
|     name: 'AppRiskArea', | ||||
|     label: '风险配置', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       return { | ||||
|         component: 'List', | ||||
|         params: {}, | ||||
|         moduleId: '', | ||||
|         include: [], | ||||
|         moduleName: '' | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     components: { | ||||
|       Add, | ||||
|       List | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       onChange (data) { | ||||
|         if (data.type === 'Add') { | ||||
|           this.component = 'Add' | ||||
|           this.params = data.params | ||||
|         } | ||||
|  | ||||
|         if (data.type === 'list') { | ||||
|           this.component = 'List' | ||||
|           this.params = data.params | ||||
|  | ||||
|           this.$nextTick(() => { | ||||
|             if (data.isRefresh) { | ||||
|               this.$refs.component.getList() | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss"> | ||||
|   .doc-circulation { | ||||
|     height: 100%; | ||||
|     background: #F3F6F9; | ||||
|     overflow: auto; | ||||
|   } | ||||
| </style> | ||||
| @@ -1,154 +0,0 @@ | ||||
| <template> | ||||
|   <ai-detail class="content-add"> | ||||
|     <template slot="title"> | ||||
|       <ai-title :title="params.id ? '编辑风险区域' : '添加风险区域'" isShowBack isShowBottomBorder @onBackClick="cancel(false)"> | ||||
|       </ai-title> | ||||
|     </template> | ||||
|     <template slot="content"> | ||||
|       <ai-card title="基本信息"> | ||||
|         <template #content> | ||||
|           <el-form class="ai-form" :model="form" label-width="120px" ref="form"> | ||||
|             <el-form-item prop="areaId" style="width: 100%;" label="选择地区" :rules="[{required: true, message: '请选择地区', trigger: 'change'}]"> | ||||
|               <ai-area-select clearable @fullname="v => form.areaName = v" always-show :instance="instance" v-model="form.areaId"></ai-area-select> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="风险等级" style="width: 100%;" prop="level" :rules="[{required: true, message: '请选择风险等级', trigger: 'change'}]"> | ||||
|               <ai-select | ||||
|                 v-model="form.level" | ||||
|                 clearable | ||||
|                 placeholder="请选择风险等级" | ||||
|                 :selectList="dict.getDict('epidemicDangerousAreaLevel')"> | ||||
|               </ai-select> | ||||
|             </el-form-item> | ||||
|           </el-form> | ||||
|         </template> | ||||
|       </ai-card> | ||||
|     </template> | ||||
|     <template #footer> | ||||
|       <el-button @click="cancel">取消</el-button> | ||||
|       <el-button type="primary" @click="confirm">提交</el-button> | ||||
|     </template> | ||||
|   </ai-detail> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import { mapState } from 'vuex' | ||||
|   export default { | ||||
|     name: 'Add', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object, | ||||
|       params: Object, | ||||
|       moduleName: String | ||||
|     }, | ||||
|     data () { | ||||
|       return { | ||||
|         info: {}, | ||||
|         form: { | ||||
|           level: '', | ||||
|           areaId: '', | ||||
|           areaName: '' | ||||
|         }, | ||||
|         id: '' | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|       ...mapState(['user']) | ||||
|     }, | ||||
|  | ||||
|     created () { | ||||
|       this.dict.load('epidemicDangerousAreaLevel').then(() => { | ||||
|         if (this.params && this.params.id) { | ||||
|           this.id = this.params.id | ||||
|           this.getInfo(this.params.id) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       getInfo (id) { | ||||
|         this.instance.post(`/app/appepidemicdangerousarea/queryDetailById?id=${id}`).then(res => { | ||||
|           if (res.code === 0) { | ||||
|             this.form = res.data | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       confirm () { | ||||
|         this.$refs.form.validate((valid) => { | ||||
|           if (valid) { | ||||
|             this.instance.post(`/app/appepidemicdangerousarea/addOrUpdate`, { | ||||
|               ...this.form | ||||
|             }).then(res => { | ||||
|               if (res.code == 0) { | ||||
|                 this.$message.success('提交成功') | ||||
|                 setTimeout(() => { | ||||
|                   this.cancel(true) | ||||
|                 }, 600) | ||||
|               } | ||||
|             }) | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       cancel (isRefresh) { | ||||
|         this.$emit('change', { | ||||
|           type: 'list', | ||||
|           isRefresh: !!isRefresh | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
|   .content-add { | ||||
|     .video { | ||||
|       width: 640px; | ||||
|       height: 360px; | ||||
|       border-radius: 4px; | ||||
|       border: 1px dashed #D0D4DC; | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       align-items: center; | ||||
|       justify-content: center; | ||||
|  | ||||
|       .icon { | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|  | ||||
|         span:nth-child(2) { | ||||
|           display: inline-block; | ||||
|           font-size: 16px; | ||||
|           color: #333333; | ||||
|           line-height: 30px; | ||||
|         } | ||||
|  | ||||
|         .iconfont { | ||||
|           display: inline-block; | ||||
|           font-size: 40px; | ||||
|           color: #2266FF; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .tips { | ||||
|         display: inline-block; | ||||
|         font-size: 12px; | ||||
|         color: #999999; | ||||
|         line-height: 26px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .video-com { | ||||
|       width: 640px; | ||||
|       height: 360px; | ||||
|       background: rgba(0, 0, 0, 0.5); | ||||
|       border-radius: 2px; | ||||
|       border: 1px solid #D0D4DC; | ||||
|       margin-top: -40px; | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
| @@ -1,141 +0,0 @@ | ||||
| <template> | ||||
|   <ai-list class="notice"> | ||||
|     <template slot="title"> | ||||
|       <ai-title title="风险区域配置" isShowBottomBorder></ai-title> | ||||
|     </template> | ||||
|     <template slot="content"> | ||||
|       <ai-search-bar class="search-bar"> | ||||
|         <template #left> | ||||
|           <ai-select | ||||
|             v-model="search.level" | ||||
|             clearable | ||||
|             placeholder="请选择风险等级" | ||||
|             :selectList="dict.getDict('epidemicDangerousAreaLevel')" | ||||
|             @change="search.current = 1, getList()"> | ||||
|           </ai-select> | ||||
|           <el-button size="small" type="primary" icon="iconfont iconAdd" @click="toAdd('')">添加</el-button> | ||||
|         </template> | ||||
|         <template #right> | ||||
|           <el-input | ||||
|             v-model="search.province" | ||||
|             class="search-input" | ||||
|             size="small" | ||||
|             v-throttle="() => {search.current = 1, getList()}" | ||||
|             placeholder="省级名称/市级名称/区级名称" | ||||
|             clearable | ||||
|             @clear="search.current = 1, search.province = '', getList()" | ||||
|             suffix-icon="iconfont iconSearch"> | ||||
|           </el-input> | ||||
|         </template> | ||||
|       </ai-search-bar> | ||||
|       <ai-table | ||||
|         :tableData="tableData" | ||||
|         :col-configs="colConfigs" | ||||
|         :total="total" | ||||
|         style="margin-top: 6px;" | ||||
|         :current.sync="search.current" | ||||
|         :size.sync="search.size" | ||||
|         @getList="getList"> | ||||
|         <el-table-column slot="options" width="140px" fixed="right" label="操作" align="center"> | ||||
|           <template slot-scope="{ row }"> | ||||
|             <div class="table-options"> | ||||
|               <el-button type="text" @click="toAdd(row.id)">编辑</el-button> | ||||
|               <el-button type="text" @click="remove(row.id)">删除</el-button> | ||||
|             </div> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|       </ai-table> | ||||
|     </template> | ||||
|   </ai-list> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import { mapState } from 'vuex' | ||||
|   export default { | ||||
|     name: 'List', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object, | ||||
|       moduleName: String | ||||
|     }, | ||||
|  | ||||
|     data() { | ||||
|       return { | ||||
|         search: { | ||||
|           current: 1, | ||||
|           size: 10, | ||||
|           level: '', | ||||
|           province: '' | ||||
|         }, | ||||
|         currIndex: -1, | ||||
|         areaList: [], | ||||
|         total: 10, | ||||
|         colConfigs: [ | ||||
|           { prop: 'province',  label: '省级', align: 'left', width: '200px' }, | ||||
|           { prop: 'city', label: '市级', align: 'center' }, | ||||
|           { prop: 'district', label: '区级', align: 'center' }, | ||||
|           { prop: 'town', label: '镇级', align: 'center' }, | ||||
|           { prop: 'village', label: '村级', align: 'center' }, | ||||
|           { prop: 'level', label: '等级', align: 'center', format: v => this.dict.getLabel('epidemicDangerousAreaLevel', v) }, | ||||
|           { prop: 'createTime', label: '设置时间', align: 'center' }, | ||||
|           { prop: 'createUserName', label: '添加人', align: 'center' }, | ||||
|           { slot: 'options', label: '操作', align: 'center' } | ||||
|         ], | ||||
|         areaName: '', | ||||
|         unitName: '', | ||||
|         tableData: [] | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|       ...mapState(['user']) | ||||
|     }, | ||||
|  | ||||
|     created() { | ||||
|       this.dict.load('epidemicDangerousAreaLevel').then(() => { | ||||
|         this.getList() | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       getList() { | ||||
|         this.instance.post(`/app/appepidemicdangerousarea/list`, null, { | ||||
|           params: { | ||||
|             ...this.search | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.tableData = res.data.records | ||||
|             this.total = res.data.total | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       remove(id) { | ||||
|         this.$confirm('确定删除该数据?').then(() => { | ||||
|           this.instance.post(`/app/appepidemicdangerousarea/delete?ids=${id}`).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               this.$message.success('删除成功!') | ||||
|               this.getList() | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       toAdd(id) { | ||||
|         this.$emit('change', { | ||||
|           type: 'Add', | ||||
|           params: { | ||||
|             id: id || '' | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .notice { | ||||
|   } | ||||
| </style> | ||||
| @@ -1,206 +0,0 @@ | ||||
| <template> | ||||
|   <section class="AppVaccination"> | ||||
|     <ai-list v-if="!showDetail"> | ||||
|       <ai-title slot="title" title="疫苗接种" isShowBottomBorder isShowArea v-model="areaId" :instance="instance" | ||||
|                 @change="page.current=1,getTableData()"/> | ||||
|       <template #blank> | ||||
|         <el-row type="flex"> | ||||
|           <div class="dataPane" v-for="(op,i) in dataPanes" :key="i"> | ||||
|             {{ [op.label, op.v].join(" ") }} | ||||
|           </div> | ||||
|         </el-row> | ||||
|         <div class="mainPane"> | ||||
|           <ai-search-bar> | ||||
|             <template #left> | ||||
|               <ai-select placeholder="接种状况" v-model="search.inoculationType" @change="page.current=1,getTableData()" | ||||
|                          :selectList="dict.getDict('vaccineInoculationType')"/> | ||||
|             </template> | ||||
|             <template #right> | ||||
|               <el-input placeholder="姓名/身份证/联系方式" | ||||
|                         v-model="search.name" | ||||
|                         size="small" | ||||
|                         clearable | ||||
|                         @clear="page.current = 1,search.name = '', getTableData()" | ||||
|                         v-throttle="() => {page.current = 1, getTableData()}" | ||||
|                         suffix-icon="iconfont iconSearch"/> | ||||
|             </template> | ||||
|           </ai-search-bar> | ||||
|           <ai-search-bar> | ||||
|             <template #left> | ||||
|               <el-button type="primary" icon="iconfont iconAdd" @click="$router.push({hash:'#add'})">添加</el-button> | ||||
|               <el-button icon="iconfont iconDelete" :disabled="!ids.length" @click="handleDelete(ids)">删除</el-button> | ||||
|             </template> | ||||
|             <template #right> | ||||
|               <ai-import :instance="instance" :dict="dict" name="疫苗接种" suffixName="xlsx" | ||||
|                          type="appvaccineinoculationuser" @onSuccess="resetSearch"/> | ||||
|               <ai-download url="/app/appvaccineinoculationuser/export" :params="{...search,areaId,ids:ids.toString()}" | ||||
|                            :instance="instance" fileName="疫苗接种导出文件"/> | ||||
|             </template> | ||||
|           </ai-search-bar> | ||||
|           <ai-table :tableData="tableData" :colConfigs="colConfigs" :total="page.total" :current.sync="page.current" | ||||
|                     :size.sync="page.size" @getList="getTableData" :dict="dict" | ||||
|                     @selection-change="v=>ids=v.map(e=>e.id)"> | ||||
|             <el-table-column slot="vaccinationDate" label="接种日期" align="center" class-name="vaccinationDate"> | ||||
|               <el-table-column label="第一次" align="center" prop="firstDate"/> | ||||
|               <el-table-column label="第二次" align="center" prop="secondDate"/> | ||||
|               <el-table-column label="第三次" align="center" prop="thirdDate"/> | ||||
|             </el-table-column> | ||||
|             <el-table-column slot="options" label="操作" align="center" fixed="right"> | ||||
|               <template slot-scope="{row:{id}}"> | ||||
|                 <el-button type="text" @click="$router.push({hash:'#add',query:{id}})">编辑</el-button> | ||||
|                 <el-button type="text" @click="handleDelete(id)">删除</el-button> | ||||
|               </template> | ||||
|             </el-table-column> | ||||
|           </ai-table> | ||||
|         </div> | ||||
|       </template> | ||||
|     </ai-list> | ||||
|     <add-vaccination v-else :dict="dict" :instance="instance"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapState} from "vuex"; | ||||
| import AddVaccination from "./addVaccination"; | ||||
| import {ID} from "dui/lib/js/utils"; | ||||
|  | ||||
| export default { | ||||
|   name: "AppVaccination", | ||||
|   components: {AddVaccination}, | ||||
|   label: "疫苗接种", | ||||
|   provide() { | ||||
|     return { | ||||
|       top: this | ||||
|     } | ||||
|   }, | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       areaId: "", | ||||
|       page: {current: 1, size: 10, total: 0}, | ||||
|       search: {inoculationType: "", name: ""}, | ||||
|       ids: [], | ||||
|       tableData: [], | ||||
|       staData: {} | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|     showDetail() { | ||||
|       return this.$route.hash == "#add" | ||||
|     }, | ||||
|     dataPanes() { | ||||
|       return [ | ||||
|         {label: "总接种人数", v: this.staData.zjzrs || 0}, | ||||
|         {label: "已接种第一针人数", v: this.staData.yjzdyzrs || 0}, | ||||
|         {label: "已接种第二针人数", v: this.staData.yjzdezrs || 0}, | ||||
|         {label: "已接种第三针人数", v: this.staData.yjzdszrs || 0}, | ||||
|       ] | ||||
|     }, | ||||
|     colConfigs() { | ||||
|       return [ | ||||
|         {type: "selection", align: 'center'}, | ||||
|         {label: "姓名", prop: "name", align: 'center'}, | ||||
|         {label: "性别", prop: "sex", dict: 'sex', align: 'center'}, | ||||
|         {label: "出生日期", prop: "birthday", align: 'center'}, | ||||
|         { | ||||
|           label: "身份证号", width: "160px", align: 'center', | ||||
|           render: (h, {row}) => h('span', null, ID.hideId(row.idNumber)) | ||||
|         }, | ||||
|         {label: "所属地区", prop: "areaName", align: 'center'}, | ||||
|         {label: "住址", prop: "address", width: "200px", align: 'center'}, | ||||
|         {label: "联系方式", prop: "phone", align: 'center'}, | ||||
|         {slot: 'vaccinationDate'}, | ||||
|         {slot: "options"}, | ||||
|       ] | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.areaId = JSON.parse(JSON.stringify(this.user.info.areaId)) | ||||
|     this.dict.load('sex', 'vaccineInoculationType') | ||||
|     this.getTableData() | ||||
|   }, | ||||
|   methods: { | ||||
|     getStaData() { | ||||
|       this.instance.post(`/app/appvaccineinoculationuser/countByAreaId`, null, { | ||||
|         params: {areaId: this.areaId} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.staData = res.data | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     getTableData() { | ||||
|       this.page.current == 1 && this.getStaData() | ||||
|       this.instance.post(`/app/appvaccineinoculationuser/list`, null, { | ||||
|         params: {...this.search, ...this.page, areaId: this.areaId} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.tableData = res.data.records | ||||
|           this.page.total = res.data.total | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleDelete(ids) { | ||||
|       ids = ids?.toString() | ||||
|       this.$confirm("确定要删除该条数据吗?").then(() => { | ||||
|         this.instance.post(`/app/appvaccineinoculationuser/delete`, null, { | ||||
|           params: {ids} | ||||
|         }).then(res => { | ||||
|           if (res?.code == 0) { | ||||
|             this.$message.success("删除成功!"); | ||||
|             this.getTableData(); | ||||
|           } | ||||
|         }) | ||||
|       }).catch(() => 0) | ||||
|     }, | ||||
|     resetSearch() { | ||||
|       this.page.current = 1 | ||||
|       this.search = {} | ||||
|       this.getTableData() | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AppVaccination { | ||||
|   height: 100%; | ||||
|  | ||||
|   :deep( .dataPane ){ | ||||
|     flex: 1; | ||||
|     min-width: 0; | ||||
|     background: #FFFFFF; | ||||
|     box-shadow: 0 4px 6px -2px rgba(15, 15, 21, 0.15); | ||||
|     border-radius: 2px; | ||||
|     margin-bottom: 16px; | ||||
|     margin-right: 16px; | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|     height: 60px; | ||||
|  | ||||
|     &:last-of-type { | ||||
|       margin-right: 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   :deep( .mainPane ){ | ||||
|     background: #fff; | ||||
|     padding: 12px 16px; | ||||
|     box-shadow: 0 4px 6px -2px rgba(15, 15, 21, 0.15); | ||||
|  | ||||
|     .vaccinationDate { | ||||
|       border-bottom: 1px solid #D0D4DC; | ||||
|     } | ||||
|  | ||||
|     .ai-table__header { | ||||
|       padding: 2px 0; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -1,236 +0,0 @@ | ||||
| <template> | ||||
|   <section class="addVaccination"> | ||||
|     <ai-detail> | ||||
|       <ai-title slot="title" :title="addTitle" isShowBottomBorder | ||||
|                 isShowBack @onBackClick="back"/> | ||||
|       <template #content> | ||||
|         <el-form size="small" :model="form" ref="vaccinationForm" :rules="rules" label-width="100px"> | ||||
|           <ai-card title="基本信息"> | ||||
|             <template #content> | ||||
|               <el-form-item label="受种人姓名" prop="name"> | ||||
|                 <el-row type="flex" align="middle"> | ||||
|                   <el-input placeholder="请输入" v-model="form.name" :disabled="isEdit" clearable | ||||
|                             style="margin-right: 8px"/> | ||||
|                   <ai-person-select v-if="!isEdit" :instance="instance" @selectPerson="handleSelectPerson"/> | ||||
|                 </el-row> | ||||
|               </el-form-item> | ||||
|               <el-form-item label="身份证号码" prop="idNumber"> | ||||
|                 <ai-id v-model="form.idNumber" @change="getInfoByIdNumber" :disabled="isEdit"/> | ||||
|               </el-form-item> | ||||
|               <el-form-item label="性别" prop="sex"> | ||||
|                 <ai-select disabled v-model="form.sex" :selectList="dict.getDict('sex')"/> | ||||
|               </el-form-item> | ||||
|               <el-form-item label="出生日期" prop="birthday"> | ||||
|                 <el-date-picker v-model="form.birthday" type="date" disabled/> | ||||
|               </el-form-item> | ||||
|               <el-form-item label="联系方式" prop="phone"> | ||||
|                 <el-input v-model="form.phone" placeholder="请输入"/> | ||||
|               </el-form-item> | ||||
|               <el-form-item label="所属地区" isLine prop="areaId"> | ||||
|                 <ai-area-select :instance="instance" v-model="form.areaId" always-show @name="v=>form.areaName=v"/> | ||||
|               </el-form-item> | ||||
|               <el-form-item label="住址" isLine prop="address"> | ||||
|                 <el-input v-model="form.address" placeholder="请输入"/> | ||||
|               </el-form-item> | ||||
|             </template> | ||||
|           </ai-card> | ||||
|           <ai-card title="接种情况"> | ||||
|             <template #right> | ||||
|               <el-button icon="iconfont iconAdd" type="text" @click="dialog=true">添加</el-button> | ||||
|             </template> | ||||
|             <template #content> | ||||
|               <el-form-item isLine label-width="0"> | ||||
|                 <ai-table :tableData="form.detailList" :colConfigs="colConfigs" :isShowPagination="false" :dict="dict"> | ||||
|                   <el-table-column slot="options" label="操作" align="center" fixed="right"> | ||||
|                     <template v-slot="{row,$index}"> | ||||
|                       <el-button type="text" @click="handleEdit(row,$index)">编辑</el-button> | ||||
|                       <el-button type="text" @click="handleDelete($index)">删除</el-button> | ||||
|                     </template> | ||||
|                   </el-table-column> | ||||
|                 </ai-table> | ||||
|               </el-form-item> | ||||
|             </template> | ||||
|           </ai-card> | ||||
|         </el-form> | ||||
|       </template> | ||||
|       <template #footer> | ||||
|         <el-button @click="back">取消</el-button> | ||||
|         <el-button type="primary" @click="handleSubmit">提交</el-button> | ||||
|       </template> | ||||
|     </ai-detail> | ||||
|     <ai-dialog :visible.sync="dialog" title="接种情况" @closed="dialogForm={}" @onConfirm="handleConfirm"> | ||||
|       <el-form ref="appvaccineinoculationuser" size="small" :model="dialogForm" label-width="100px" :rules="rules"> | ||||
|         <el-form-item label="接种次数" prop="type"> | ||||
|           <ai-select v-model="dialogForm.type" :selectList="dict.getDict('vaccineType')"/> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="接种日期" prop="vaccinateDate"> | ||||
|           <el-date-picker v-model="dialogForm.vaccinateDate" value-format="yyyy-MM-dd"/> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="接种人员" prop="vaccinatePerson"> | ||||
|           <el-input v-model="dialogForm.vaccinatePerson"/> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="生产企业" prop="createCompany"> | ||||
|           <el-input v-model="dialogForm.createCompany"/> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="接种单位" prop="vaccinateUnit"> | ||||
|           <el-input v-model="dialogForm.vaccinateUnit"/> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|     </ai-dialog> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapState} from "vuex"; | ||||
| import {ID} from "dui/lib/js/utils"; | ||||
|  | ||||
| export default { | ||||
|   name: "addVaccination", | ||||
|   inject: ['top'], | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       form: {detailList: []}, | ||||
|       dialog: false, | ||||
|       dialogForm: {} | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|     isEdit() { | ||||
|       return !!this.$route.query.id | ||||
|     }, | ||||
|     addTitle() { | ||||
|       return this.isEdit ? '编辑疫苗接种人员' : '新增疫苗接种人员' | ||||
|     }, | ||||
|     rules() { | ||||
|       return { | ||||
|         vaccinateDate: {required: true, message: "请选择 接种日期"}, | ||||
|         type: {required: true, message: "请选择 接种次数",}, | ||||
|         areaId: [ | ||||
|           {required: true, message: "请选择 所属地区"}, | ||||
|           {trigger:'blur',validator: (r, v, cb) => /0{3}$/g.test(v) ? cb('请选择到村/社区') : cb()} | ||||
|         ], | ||||
|         name: {required: true, message: "请填写 受种人姓名"}, | ||||
|         idNumber: {required: true, message: "请填写 身份号码"}, | ||||
|         sex: {required: true, message: "请填写 性别"}, | ||||
|         birthday: {required: true, message: "请填写 出生日期"}, | ||||
|         phone: {required: true, message: "请填写 联系方式"}, | ||||
|       } | ||||
|     }, | ||||
|     colConfigs() { | ||||
|       return [ | ||||
|         {label: "类型", align: 'center', prop: "type", dict: 'vaccineType'}, | ||||
|         {label: "接种日期", align: 'center', prop: "vaccinateDate"}, | ||||
|         {label: "接种人员", align: 'center', prop: "vaccinatePerson"}, | ||||
|         {label: "生产企业", align: 'center', prop: "createCompany"}, | ||||
|         {label: "接种单位", align: 'center', prop: "vaccinateUnit"}, | ||||
|         {slot: "options"} | ||||
|       ] | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     back() { | ||||
|       this.$router.push({}) | ||||
|     }, | ||||
|     getDetail() { | ||||
|       let {id} = this.$route.query | ||||
|       if (id) { | ||||
|         this.instance.post("/app/appvaccineinoculationuser/queryDetailById", null, { | ||||
|           params: {id} | ||||
|         }).then(res => { | ||||
|           if (res?.data) { | ||||
|             this.form = res.data | ||||
|           } | ||||
|         }) | ||||
|       } else { | ||||
|         this.$set(this.form,'areaId',JSON.parse(JSON.stringify(this.top.areaId))) | ||||
|       } | ||||
|     }, | ||||
|     handleSubmit() { | ||||
|       this.$refs.vaccinationForm?.validate(v => { | ||||
|         if (v) { | ||||
|           this.instance.post("/app/appvaccineinoculationuser/addOrUpdate", this.form).then(res => { | ||||
|             if (res?.code == 0) { | ||||
|               this.$message.success("提交成功!") | ||||
|               this.top.resetSearch() | ||||
|               this.back() | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleEdit(row, index) { | ||||
|       this.dialogForm = JSON.parse(JSON.stringify({...row, index})) | ||||
|       this.dialog = true | ||||
|     }, | ||||
|     handleConfirm() { | ||||
|       this.$refs.appvaccineinoculationuser.validate(v => { | ||||
|         if (v) { | ||||
|           if (this.dialogForm.index > -1) { | ||||
|             this.form.detailList.splice(this.dialogForm.index, 1, this.dialogForm) | ||||
|           } else { | ||||
|             this.form.detailList.push(this.dialogForm) | ||||
|           } | ||||
|           this.dialog = false | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleSelectPerson(v) { | ||||
|       let {name, idNumber, phone, currentAreaId:areaId, currentAddress:address} = v | ||||
|       this.form = {...this.form, name, idNumber, phone, areaId, address} | ||||
|     }, | ||||
|     getInfoByIdNumber(code) { | ||||
|       if (ID.check(code)) { | ||||
|         let info = new ID(code) | ||||
|         this.form.sex = info.sex | ||||
|         this.form.birthday = info.birthday | ||||
|         this.$forceUpdate() | ||||
|       } | ||||
|     }, | ||||
|     handleDelete(index) { | ||||
|       this.$confirm("是否要删除该条数据?").then(() => this.form.detailList.splice(index, 1)).catch(() => 0) | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.dict.load('vaccineType') | ||||
|     this.getDetail() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .addVaccination { | ||||
|   height: 100%; | ||||
|  | ||||
|   :deep( .ai-card__body), .el-form { | ||||
|     display: flex; | ||||
|     flex-wrap: wrap; | ||||
|   } | ||||
|  | ||||
|   .ai-card { | ||||
|     width: 100%; | ||||
|   } | ||||
|  | ||||
|   .el-form-item { | ||||
|     width: 50%; | ||||
|  | ||||
|     &[isLine] { | ||||
|       width: 100%; | ||||
|     } | ||||
|  | ||||
|     .el-date-editor { | ||||
|       width: 100%; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   :deep( .el-button ){ | ||||
|     .iconfont { | ||||
|       color: inherit | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -1,66 +0,0 @@ | ||||
| <template> | ||||
|   <div class="doc-circulation ailist-wrapper"> | ||||
|     <keep-alive :include="['List']"> | ||||
|       <component ref="component" :is="component" @change="onChange" :params="params" :instance="instance" :dict="dict"></component> | ||||
|     </keep-alive> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import List from './components/List' | ||||
|   import Add from './components/Add' | ||||
|  | ||||
|   export default { | ||||
|     name: 'AppAddressBook', | ||||
|     label: '通讯录管理', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       return { | ||||
|         component: 'List', | ||||
|         params: {}, | ||||
|         include: [] | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     components: { | ||||
|       Add, | ||||
|       List | ||||
|     }, | ||||
|  | ||||
|     mounted () { | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       onChange (data) { | ||||
|         if (data.type === 'Add') { | ||||
|           this.component = 'Add' | ||||
|           this.params = data.params | ||||
|         } | ||||
|  | ||||
|         if (data.type === 'list') { | ||||
|           this.component = 'List' | ||||
|           this.params = data.params | ||||
|  | ||||
|           this.$nextTick(() => { | ||||
|             if (data.isRefresh) { | ||||
|               this.$refs.component.getList() | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss"> | ||||
|   .doc-circulation { | ||||
|     height: 100%; | ||||
|     background: #F3F6F9; | ||||
|     overflow: auto; | ||||
|   } | ||||
| </style> | ||||
| @@ -1,203 +0,0 @@ | ||||
| <template> | ||||
|   <ai-detail> | ||||
|     <template slot="title"> | ||||
|       <ai-title :title="id ? '编辑成员' : '添加成员'" isShowBack isShowBottomBorder @onBackClick="cancel(false)"> | ||||
|       </ai-title> | ||||
|     </template> | ||||
|     <template slot="content">   | ||||
|       <el-form ref="form" :model="form" label-width="110px" label-position="right"> | ||||
|       <ai-card title="个人信息"> | ||||
|         <template #content> | ||||
|           <div class="ai-form"> | ||||
|             <el-form-item label="姓名" prop="name" :rules="[{ required: true, message: '请输入姓名', trigger: 'blur' }]"> | ||||
|               <el-input size="small" placeholder="请输入姓名" show-word-limit v-model="form.name" :maxlength="10"></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="账号" prop="id" :rules="[{ required: true, message: '请输入账号', trigger: 'blur' }]"> | ||||
|               <el-input size="small" :disabled="!!id" show-word-limit :maxlength="30" placeholder="成员唯一标识,设定以后不支持修改" v-model="form.id"></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="手机号" prop="mobile" :rules="[{ required: true, validator: validatorPhone, trigger: 'blur' }]"> | ||||
|               <el-input size="small" placeholder="请输入手机号" show-word-limit :maxlength="11" v-model="form.mobile"></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="性别" prop="gender"> | ||||
|               <el-radio-group v-model="form.gender"> | ||||
|                 <el-radio label="1">男</el-radio> | ||||
|                 <el-radio label="2">女</el-radio> | ||||
|               </el-radio-group> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="座机" prop="telephone"> | ||||
|               <el-input size="small" placeholder="请输入座机" v-model="form.telephone"></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="邮箱" prop="email"> | ||||
|               <el-input size="small" placeholder="请输入邮箱" v-model="form.email"></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="地址" style="width: 100%;" prop="address"> | ||||
|               <el-input size="small" style="width: 100%;" show-word-limit :maxlength="30" placeholder="请输入地址" v-model="form.address"></el-input> | ||||
|             </el-form-item> | ||||
|           </div> | ||||
|         </template> | ||||
|       </ai-card> | ||||
|       <ai-card title="组织信息"> | ||||
|         <template #content> | ||||
|           <el-form-item label="部门" prop="departmentName" style="width: 100%;" :rules="[{ required: true, message: '请选择部门', trigger: 'change' }]"> | ||||
|             <el-input size="small" placeholder="请选择..." disabled v-model="form.departmentName"> | ||||
|               <ai-wechat-selecter slot="append" isStrictly :instance="instance" @change="onChange" v-model="department" isChooseUnit> | ||||
|                 <el-button type="info">选择</el-button> | ||||
|               </ai-wechat-selecter> | ||||
|             </el-input> | ||||
|           </el-form-item> | ||||
|           <el-form-item label="标签" style="width: 100%;" prop="tags"> | ||||
|             <el-select size="small" v-model="form.tagIds" multiple placeholder="请选择标签"> | ||||
|               <el-option | ||||
|                 v-for="item in tagsList" | ||||
|                 :key="item.id" | ||||
|                 :label="item.tagname" | ||||
|                 :value="item.id"> | ||||
|               </el-option> | ||||
|             </el-select> | ||||
|           </el-form-item> | ||||
|           <el-form-item label="职务" prop="position"> | ||||
|             <el-input size="small" placeholder="请输入职务" v-model="form.position"></el-input> | ||||
|           </el-form-item> | ||||
|         </template> | ||||
|       </ai-card> | ||||
|       </el-form> | ||||
|     </template> | ||||
|     <template #footer> | ||||
|       <el-button @click="cancel">取消</el-button> | ||||
|       <el-button type="primary" @click="confirm">提交</el-button> | ||||
|     </template> | ||||
|   </ai-detail> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   export default { | ||||
|     name: 'Add', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object, | ||||
|       params: Object | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       const validatorPhone = function (rule, value, callback) { | ||||
|         if (value === '') { | ||||
|           callback(new Error('请输入手机号')) | ||||
|         } else if (!/^1\d{10}$/.test(value)) { | ||||
|           callback(new Error('手机号格式错误')) | ||||
|         } else { | ||||
|           callback() | ||||
|         } | ||||
|       } | ||||
|       return { | ||||
|         info: {}, | ||||
|         department: [], | ||||
|         validatorPhone: validatorPhone, | ||||
|         form: { | ||||
|           position: '', | ||||
|           name: '', | ||||
|           email: '', | ||||
|           telephone: '', | ||||
|           gender: '', | ||||
|           mobile: '', | ||||
|           departmentName: '', | ||||
|           departmentIds: [], | ||||
|           tagIds: [], | ||||
|           id: '' | ||||
|         }, | ||||
|         id: '', | ||||
|         tagsList: [] | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     created () { | ||||
|       this.getTags() | ||||
|  | ||||
|       if (this.params && this.params.departmentId && !this.params.id) { | ||||
|         this.department = [{ | ||||
|           id: String(this.params.departmentId), | ||||
|           name: this.params.departmentName | ||||
|         }] | ||||
|         this.form.departmentIds = [this.params.departmentId] | ||||
|         this.form.departmentName = this.params.departmentName | ||||
|       } | ||||
|  | ||||
|       if (this.params && this.params.id) { | ||||
|         this.id = this.params.id | ||||
|         this.getInfo(this.params.id) | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       getInfo (id) { | ||||
|         this.instance.post(`/app/wxcp/wxuser/queryDetailById?id=${id}`).then(res => { | ||||
|           if (res.code === 0) { | ||||
|             const departmentNames = res.data.departmentNames.split(',') | ||||
|             this.department = res.data.departmentIdsStr.split(',').map((item, index) => { | ||||
|               return { | ||||
|                 name: departmentNames[index], | ||||
|                 id: item | ||||
|               } | ||||
|             }) | ||||
|             this.form = { | ||||
|               ...res.data, | ||||
|               departmentName: res.data.departmentNames, | ||||
|               tagIds: res.data.tags.map(v => v.id), | ||||
|               departmentIds: res.data.departmentIdsStr.split(',') | ||||
|             } | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       onChange (e) { | ||||
|         if (e.length) { | ||||
|           this.form.departmentIds = e.map(v => v.id) | ||||
|           this.form.departmentName = e.map(v => v.name).join(',') | ||||
|         } else { | ||||
|           this.form.departmentIds = '' | ||||
|           this.form.departmentName = '' | ||||
|         } | ||||
|       }, | ||||
|  | ||||
|       getTags () { | ||||
|         this.instance.post(`/app/wxcp/wxtag/listAll`).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.tagsList = res.data | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       onClose () { | ||||
|         this.form.explain = '' | ||||
|       }, | ||||
|  | ||||
|       confirm () { | ||||
|         this.$refs.form.validate((valid) => { | ||||
|           if (valid) { | ||||
|             const api = this.id ? '/app/wxcp/wxuser/update' : '/app/wxcp/wxuser/add' | ||||
|             this.instance.post(api, { | ||||
|               ...this.form | ||||
|             }).then(res => { | ||||
|               if (res.code == 0) { | ||||
|                 this.$message.success('提交成功') | ||||
|                 setTimeout(() => { | ||||
|                   this.cancel(true) | ||||
|                 }, 600) | ||||
|               } | ||||
|             }) | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       cancel (isRefresh) { | ||||
|         this.$emit('change', { | ||||
|           type: 'list', | ||||
|           isRefresh: !!isRefresh | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| </style> | ||||
| @@ -1,995 +0,0 @@ | ||||
| <template> | ||||
|   <ai-list class="addressBook"> | ||||
|     <template slot="title"> | ||||
|       <ai-title title="内部通讯录" isShowBottomBorder></ai-title> | ||||
|     </template> | ||||
|     <template #left> | ||||
|       <div class="addressBook-left"> | ||||
|         <div class="addressBook-left__title"> | ||||
|           <h2 @click="tabIndex = 0, search.current = 1, getList()" :class="[tabIndex === 0 ? 'tab-active' : '']"> | ||||
|             组织架构</h2> | ||||
|           <h2 @click="tabIndex = 1, search.current = 1, getList()" :class="[tabIndex === 1 ? 'tab-active' : '']">标签</h2> | ||||
|         </div> | ||||
|         <div class="addressBook-left__list--title" v-if="tabIndex === 0"> | ||||
|           <el-input | ||||
|               size="mini" | ||||
|               placeholder="请输入部门名称" | ||||
|               v-model="unitName" | ||||
|               clearable | ||||
|               suffix-icon="iconfont iconSearch"> | ||||
|           </el-input> | ||||
|         </div> | ||||
|         <div class="addressBook-left__list--title" v-if="tabIndex === 1"> | ||||
|           <el-button size="mini" icon="iconfont iconAdd" @click="isShowTags = true">添加标签</el-button> | ||||
|           <el-input | ||||
|               class="addressBook-left__list--search" | ||||
|               size="mini" | ||||
|               clearable | ||||
|               style="width: 154px;" | ||||
|               placeholder="请输入标签名称" | ||||
|               v-model="tagName" | ||||
|               suffix-icon="iconfont iconSearch"> | ||||
|           </el-input> | ||||
|         </div> | ||||
|         <div class="addressBook-left__list--wrapper"> | ||||
|           <div class="addressBook-left__list" v-show="tabIndex === 0"> | ||||
|             <el-tree | ||||
|               :filter-node-method="filterNode" | ||||
|               ref="tree" | ||||
|               :props="defaultProps" | ||||
|               node-key="id" | ||||
|               :data="unitList" | ||||
|               highlight-current | ||||
|               @node-contextmenu="nodeContextmenu" | ||||
|               :current-node-key="search.departmentId" | ||||
|               :default-expanded-keys="defaultExpanded" | ||||
|               :default-checked-keys="defaultChecked" | ||||
|               @current-change="onTreeChange"> | ||||
|             </el-tree> | ||||
|             <ul | ||||
|                 v-if="isShowMenu" | ||||
|                 class="el-dropdown-menu el-popper" | ||||
|                 :style="{top: menuInfo.y + 'px', left: menuInfo.x + 'px', position: 'fixed', zIndex: 2023}" | ||||
|                 x-placement="top-end"> | ||||
|               <li class="el-dropdown-menu__item" @click="handleTreeCommand('add', menuInfo.node)">添加子部门</li> | ||||
|               <li class="el-dropdown-menu__item" @click="handleTreeCommand('edit', menuInfo.node)">修改名称</li> | ||||
|               <li class="el-dropdown-menu__item" @click="handleTreeCommand('remove', menuInfo.node)">删除</li> | ||||
|               <li class="el-dropdown-menu__item" :class="[!menuInfo.node.i ? 'is-disabled' : '']" | ||||
|                   @click="handleTreeCommand('top', menuInfo.node)">上移 | ||||
|               </li> | ||||
|               <li | ||||
|                   class="el-dropdown-menu__item" | ||||
|                   :class="[(menuInfo.node.i === menuInfo.node.len - 1) || (!menuInfo.node.i && menuInfo.node.i !== 0) ? 'is-disabled' : '']" | ||||
|                   @click="handleTreeCommand('bottom', menuInfo.node)">下移 | ||||
|               </li> | ||||
|             </ul> | ||||
|           </div> | ||||
|           <div class="addressBook-left__list" v-show="tabIndex === 1"> | ||||
|             <div class="addressBook-left__tags"> | ||||
|               <div | ||||
|                   @click="changeTag(index)" | ||||
|                   class="addressBook-left__tags--item" | ||||
|                   :class="[currIndex === index ? 'addressBook-left__tags--item-active' : '']" | ||||
|                   v-for="(item, index) in tagsList" :key="index"> | ||||
|                 <span>{{ item.tagname }}</span> | ||||
|                 <el-dropdown @command="e => handleCommand(e, item)"> | ||||
|                   <i class="iconfont iconmore"></i> | ||||
|                   <el-dropdown-menu slot="dropdown"> | ||||
|                     <el-dropdown-item command="edit">编辑</el-dropdown-item> | ||||
|                     <el-dropdown-item command="remove">删除</el-dropdown-item> | ||||
|                   </el-dropdown-menu> | ||||
|                 </el-dropdown> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </template> | ||||
|     <template slot="content"> | ||||
|       <ai-search-bar class="search-bar"> | ||||
|         <template #left> | ||||
|           <el-button size="small" type="primary" icon="iconfont iconAdd" v-if="tabIndex === 0" @click="toAdd('')">添加成员 | ||||
|           </el-button> | ||||
|           <ai-import :instance="instance" :dict="dict" v-if="tabIndex === 0" type="wxcp/wxuser" name="内部通讯录" | ||||
|                      :importParams="{departmentId:search.departmentId}" @success="getList"/> | ||||
|           <el-button size="small" icon="iconfont iconUpdate_Files" v-if="tabIndex === 0" :loading="btnLoading" @click="syncMembers">同步部门</el-button> | ||||
|           <el-button size="small" icon="iconfont iconUpdate_Files" v-if="tabIndex === 0" :loading="btnLoading" @click="syncUser">同步成员</el-button> | ||||
|           <ai-wechat-selecter refs="addTags" :instance="instance" v-model="users" @change="onChooseUser" | ||||
|                               :disabled="currIndex < 0" v-if="tabIndex === 1"> | ||||
|             <el-button size="small" :disabled="currIndex < 0" type="primary" icon="iconfont iconAdd">添加成员</el-button> | ||||
|           </ai-wechat-selecter> | ||||
|         </template> | ||||
|         <template slot="right"> | ||||
|           <el-input | ||||
|               v-model="search.name" | ||||
|               size="small" | ||||
|               v-throttle="() => {search.current = 1, getList()}" | ||||
|               placeholder="请输入成员姓名、手机号或标签名称" | ||||
|               clearable | ||||
|               @clear="search.current = 1, search.name = '', getList()" | ||||
|               suffix-icon="iconfont iconSearch"> | ||||
|           </el-input> | ||||
|         </template> | ||||
|       </ai-search-bar> | ||||
|       <ai-table | ||||
|           :tableData="tableData" | ||||
|           :col-configs="colConfigs" | ||||
|           :total="total" | ||||
|           v-loading="loading" | ||||
|           style="margin-top: 6px;" | ||||
|           :current.sync="search.current" | ||||
|           :size.sync="search.size" | ||||
|           @handleSelectionChange="handleSelectionChange" | ||||
|           @getList="getList"> | ||||
|         <el-table-column slot="avatar" label="" align="right" width="100px"> | ||||
|           <template slot-scope="{ row }"> | ||||
|             <img class="table-avatar" :src="row.avatar || 'https://cdn.cunwuyun.cn/dvcp/h5/defaultAvatar.png'"> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column slot="tags" label="标签" align="left"> | ||||
|           <template slot-scope="{ row }"> | ||||
|             <div class="table-tags" v-if="row.tagNames"> | ||||
|               <el-tag type="info" v-for="(item, index) in row.tagNames.split('、')" size="small" :key="index">{{ | ||||
|                   item | ||||
|                 }} | ||||
|               </el-tag> | ||||
|             </div> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column slot="options" width="140px" fixed="right" label="操作" align="center"> | ||||
|           <template slot-scope="{ row }"> | ||||
|             <div class="table-options"> | ||||
|               <el-button type="text" @click="toAdd(row.id)" v-show="tabIndex === 0">编辑</el-button> | ||||
|               <el-button type="text" @click="remove(row.id)" v-show="tabIndex === 0">删除</el-button> | ||||
|               <el-button type="text" @click="removeTags(row.id)" v-show="tabIndex === 1">移除</el-button> | ||||
|             </div> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <div slot="paginationBtns" class="addressBook-table__btns"> | ||||
|           <span style="margin-right: 8px;" @click="removeAll">{{ tabIndex === 0 ? '批量删除' : '批量移除' }}</span> | ||||
|           <ai-wechat-selecter :instance="instance" v-model="department" isChooseUnit @change="onDepartment" | ||||
|                               v-if="tabIndex === 0"> | ||||
|             <span>批量导出</span> | ||||
|           </ai-wechat-selecter> | ||||
|         </div> | ||||
|       </ai-table> | ||||
|       <ai-dialog | ||||
|           :visible.sync="isShowTags" | ||||
|           width="590px" | ||||
|           :title="tagId ? '编辑标签' : '添加标签'" | ||||
|           @close="onClose" | ||||
|           @onConfirm="onFormConfirm"> | ||||
|         <el-form ref="tagForm" :model="tagForm" label-width="110px" label-position="right"> | ||||
|           <el-form-item label="标签名称" prop="tagname" :rules="[{ required: true, message: '请输入标签名称', trigger: 'blur' }]"> | ||||
|             <el-input size="small" placeholder="请输入标签名称" show-word-limit :maxlength="10" v-model="tagForm.tagname"></el-input> | ||||
|           </el-form-item> | ||||
|         </el-form> | ||||
|       </ai-dialog> | ||||
|       <ai-dialog | ||||
|           :visible.sync="isShowDepart" | ||||
|           width="590px" | ||||
|           :title="departId ? '修改名称' : '添加部门'" | ||||
|           @close="onClose" | ||||
|           @onConfirm="onDepartConfirm"> | ||||
|         <el-form ref="departForm" :model="departForm" label-width="110px" label-position="right"> | ||||
|           <el-form-item label="部门名称" prop="name" :rules="[{ required: true, message: '请输入部门名称', trigger: 'blur' }]"> | ||||
|             <el-input size="small" placeholder="请输入部门名称" show-word-limit :maxlength="30" v-model="departForm.name"></el-input> | ||||
|           </el-form-item> | ||||
|         </el-form> | ||||
|       </ai-dialog> | ||||
|     </template> | ||||
|   </ai-list> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapState} from 'vuex' | ||||
|  | ||||
| export default { | ||||
|   name: 'List', | ||||
|  | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object | ||||
|   }, | ||||
|  | ||||
|   data() { | ||||
|     return { | ||||
|       users: [], | ||||
|       isShowMenu: false, | ||||
|       department: [], | ||||
|       btnLoading: false, | ||||
|       menuInfo: { | ||||
|         x: '', | ||||
|         y: '', | ||||
|         node: {} | ||||
|       }, | ||||
|       search: { | ||||
|         current: 1, | ||||
|         size: 10, | ||||
|         title: '', | ||||
|         tagname: '', | ||||
|         name: '', | ||||
|         tagIds: '', | ||||
|         departmentId: '' | ||||
|       }, | ||||
|       tagForm: { | ||||
|         tagname: '' | ||||
|       }, | ||||
|       isShowDepart: false, | ||||
|       departForm: { | ||||
|         name: '' | ||||
|       }, | ||||
|       loading: false, | ||||
|       isShowTags: false, | ||||
|       defaultChecked: [], | ||||
|       defaultExpanded: [], | ||||
|       tabIndex: 0, | ||||
|       currIndex: -1, | ||||
|       areaList: [], | ||||
|       total: 0, | ||||
|       colConfigs: [ | ||||
|         {type: 'selection', label: ''}, | ||||
|         {slot: 'avatar', label: ''}, | ||||
|         {prop: 'name', label: '姓名'}, | ||||
|         {prop: 'position', label: '职务'}, | ||||
|         {prop: 'departmentNames', label: '部门'}, | ||||
|         {prop: 'mobile', label: '手机号'}, | ||||
|         {slot: 'tags', label: '标签'}, | ||||
|         {prop: 'status', label: '账号状态', align: 'center', format: v => v == 1 ? '已激活' : '未激活'} | ||||
|       ], | ||||
|       defaultProps: { | ||||
|         children: 'children', | ||||
|         label: 'name' | ||||
|       }, | ||||
|       unitName: '', | ||||
|       rootId: '', | ||||
|       unitList: [], | ||||
|       tagsList: [], | ||||
|       tagName: '', | ||||
|       sourceTagList: [], | ||||
|       tableData: [], | ||||
|       tagId: '', | ||||
|       departmentName: '', | ||||
|       departId: '', | ||||
|       ids: '' | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   computed: { | ||||
|     ...mapState(['user']) | ||||
|   }, | ||||
|  | ||||
|   watch: { | ||||
|     unitName(val) { | ||||
|       this.$refs.tree.filter(val) | ||||
|     }, | ||||
|  | ||||
|     tagName(val) { | ||||
|       if (!val) { | ||||
|         this.tagsList = this.sourceTagList | ||||
|       } | ||||
|  | ||||
|       this.tagsList = this.sourceTagList.filter(v => v.tagname.indexOf(val) > -1) | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   mounted() { | ||||
|     this.getTree() | ||||
|     this.getList() | ||||
|     this.getTags() | ||||
|     document.querySelector('html').addEventListener('click', this.bindEvent) | ||||
|  | ||||
|     this.$nextTick(() => { | ||||
|     }) | ||||
|   }, | ||||
|  | ||||
|   methods: { | ||||
|     changeTag(index) { | ||||
|       this.currIndex = index | ||||
|       this.search.current = 1 | ||||
|  | ||||
|       this.$nextTick(() => { | ||||
|         this.getList() | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     bindEvent() { | ||||
|       this.isShowMenu = false | ||||
|     }, | ||||
|  | ||||
|     nodeContextmenu(e, node) { | ||||
|       this.isShowMenu = true | ||||
|       let y = e.y + 6 | ||||
|       if (y + 202 > document.body.clientHeight) { | ||||
|         y = y - 202 | ||||
|       } | ||||
|       this.menuInfo = { | ||||
|         x: e.x + 16, y, | ||||
|         node | ||||
|       } | ||||
|     }, | ||||
|     removeTags(id) { | ||||
|       if (this.currIndex < 0) { | ||||
|         return this.$message.error('请选择标签') | ||||
|       } | ||||
|  | ||||
|       this.$confirm('确定移除该成员?').then(() => { | ||||
|         this.instance.post(`/app/wxcp/wxtag/removeTag?userIds=${id}&tagId=${this.tagsList[this.currIndex].id}`).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.$message.success('移除成功!') | ||||
|             this.search.current = 1 | ||||
|  | ||||
|             this.getList() | ||||
|           } | ||||
|         }) | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     onDepartment(e) { | ||||
|       if (!e.length) { | ||||
|         return this.$message.error('请选择部门') | ||||
|       } | ||||
|       const ids = e.map(v => v.id).join(',') | ||||
|  | ||||
|       this.department = [] | ||||
|       this.instance.post(`/app/wxcp/wxuser/export?departmentId=${ids}`, null, { | ||||
|         responseType: 'blob' | ||||
|       }).then(res => { | ||||
|         if (res?.type == "application/json") { | ||||
|           let reader = new FileReader() | ||||
|           reader.readAsText(res, "utf-8") | ||||
|           reader.onload = e => { | ||||
|             if (e.target.readyState === 2) { | ||||
|               let ret = JSON.parse(e.target.result) | ||||
|               if (ret?.code == 0) { | ||||
|                 this.$message.success(ret.msg) | ||||
|               } else this.$message.error(ret.msg) | ||||
|             } | ||||
|           } | ||||
|         } else { | ||||
|           const link = document.createElement('a') | ||||
|           let blob = new Blob([res], {type: res.type}) | ||||
|           link.style.display = 'none' | ||||
|           link.href = URL.createObjectURL(blob) | ||||
|           link.setAttribute('download', `${e[0].name}.xls`) | ||||
|           document.body.appendChild(link) | ||||
|           link.click() | ||||
|           document.body.removeChild(link) | ||||
|           this.$message.success('导出成功!') | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     onChooseUser(e) { | ||||
|       if (!e.length) { | ||||
|         return this.$message.error('请选择成员') | ||||
|       } | ||||
|  | ||||
|       this.instance.post(`/app/wxcp/wxtag/markTag`, null, { | ||||
|         params: { | ||||
|           tagId: this.tagsList[this.currIndex].id, | ||||
|           userIds: e.map(v => v.id).join(',') | ||||
|         } | ||||
|       }).then(res => { | ||||
|         if (res.code == 0) { | ||||
|           this.getList() | ||||
|           this.users = [] | ||||
|           this.search.current = 1 | ||||
|           this.$refs.addTags.reset() | ||||
|         } else { | ||||
|           this.users = [] | ||||
|         } | ||||
|       }).catch(() => { | ||||
|         this.users = [] | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     handleTreeCommand(e, item) { | ||||
|       this.isShowMenu = false | ||||
|  | ||||
|       if (e === 'add') { | ||||
|         this.departForm.id = '' | ||||
|         this.departForm.parentid = item.id | ||||
|         this.departId = '' | ||||
|         this.isShowDepart = true | ||||
|       } else if (e === 'edit') { | ||||
|         this.departForm = { | ||||
|           ...item | ||||
|         } | ||||
|  | ||||
|         this.departId = item.id | ||||
|         this.isShowDepart = true | ||||
|       } else if (e === 'top') { | ||||
|         if (!item.i) { | ||||
|           return false | ||||
|         } | ||||
|  | ||||
|         this.moveDepart(item.id, 0) | ||||
|       } else if (e === 'bottom') { | ||||
|         if ((item.i === item.len - 1) || (!item.i && item.i !== 0)) { | ||||
|           return false | ||||
|         } | ||||
|  | ||||
|         this.moveDepart(item.id, 1) | ||||
|       } else if (e === 'remove') { | ||||
|         this.removeDepart(item.id, item.parentid) | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     removeDepart(id, parentid) { | ||||
|       this.$confirm('确定删除该数据?').then(() => { | ||||
|         this.instance.post(`/app/wxcp/wxdepartment/delete?id=${id}`).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.defaultChecked = [parentid] | ||||
|             this.$message.success('删除成功!') | ||||
|             this.getTree() | ||||
|           } | ||||
|         }) | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     moveDepart(id, type) { | ||||
|       this.instance.post(`/app/wxcp/wxdepartment/move?id=${id}&type=${type}`).then(res => { | ||||
|         if (res.code == 0) { | ||||
|           this.defaultChecked = [id] | ||||
|           this.getTree() | ||||
|           this.$message.success(type === 0 ? '上移成功' : '下移成功') | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     onDepartConfirm() { | ||||
|       this.$refs.departForm.validate((valid) => { | ||||
|         if (valid) { | ||||
|           this.instance.post(`/app/wxcp/wxdepartment/addOrUpdate`, { | ||||
|             ...this.departForm, | ||||
|             departId: this.departId | ||||
|           }).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               this.defaultChecked = [this.departForm.parentid] | ||||
|               this.isShowDepart = false | ||||
|               this.getTree() | ||||
|               this.$message.success(this.departId ? '编辑成功' : '新增成功') | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     onFormConfirm() { | ||||
|       this.$refs.tagForm.validate((valid) => { | ||||
|         if (valid) { | ||||
|           this.instance.post(`/app/wxcp/wxtag/addOrUpdate`, { | ||||
|             ...this.tagForm | ||||
|           }).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               this.isShowTags = false | ||||
|               this.getTags() | ||||
|               this.$message.success(this.tagId ? '编辑成功' : '新增成功') | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     handleCommand(e, item) { | ||||
|       if (e === 'edit') { | ||||
|         this.tagId = item.id | ||||
|         this.tagForm = { | ||||
|           ...item | ||||
|         } | ||||
|  | ||||
|         this.isShowTags = true | ||||
|       } else { | ||||
|         this.$confirm('确定删除该数据?').then(() => { | ||||
|           this.instance.post(`/app/wxcp/wxtag/delete?id=${item.id}`).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               this.$message.success('删除成功!') | ||||
|               this.getTags() | ||||
|               this.getList() | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     onClose() { | ||||
|       this.tagId = '' | ||||
|       this.tagForm.tagname = '' | ||||
|       this.departForm.name = '' | ||||
|       this.departId = '' | ||||
|       this.departForm.nameEn = '' | ||||
|       this.departForm.parentid = '' | ||||
|       this.departForm.showIndex = '' | ||||
|     }, | ||||
|  | ||||
|     syncMembers() { | ||||
|       this.btnLoading = true | ||||
|  | ||||
|       this.instance.post(`/app/wxcp/wxdepartment/syncDepart`).then(res => { | ||||
|         if (res.code == 0) { | ||||
|           this.$message.success('同步成功') | ||||
|           this.getList() | ||||
|           this.getTree() | ||||
|         } | ||||
|  | ||||
|         this.btnLoading = false | ||||
|       }).catch(() => { | ||||
|         this.btnLoading = false | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     syncUser() { | ||||
|       let departId = this.search.departmentId; | ||||
|       if (!departId) departId = 1; | ||||
|       this.btnLoading = true | ||||
|  | ||||
|       this.instance.post(`/app/wxcp/wxdepartment/syncUser?departmentId=${departId}`, null, { | ||||
|         timeout: 1000000 | ||||
|       }).then(res => { | ||||
|         if (res.code == 0) { | ||||
|           this.$message.success('同步成功') | ||||
|           this.getList() | ||||
|           this.getTree() | ||||
|         } | ||||
|  | ||||
|         this.btnLoading = false | ||||
|       }).catch(() => { | ||||
|         this.btnLoading = false | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     getTags() { | ||||
|       this.instance.post(`/app/wxcp/wxtag/listAll`).then(res => { | ||||
|         if (res.code == 0) { | ||||
|           this.sourceTagList = res.data.length ? JSON.parse(JSON.stringify(res.data)) : [] | ||||
|           this.tagsList = res.data | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     onSwitchChange(id) { | ||||
|       this.instance.post(`/app/wxcp/wxuser/enable?id=${id}`).then(res => { | ||||
|         if (res.code == 0) { | ||||
|           this.getList() | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     onTreeChange(e) { | ||||
|       this.departmentName = e.name | ||||
|       this.search.departmentId = e.id || '' | ||||
|       this.search.current = 1 | ||||
|       this.isShowMenu = false | ||||
|  | ||||
|       this.$nextTick(() => { | ||||
|         this.getList() | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     getList() { | ||||
|       this.loading = true | ||||
|       this.instance.post(`/app/wxcp/wxuser/list`, null, { | ||||
|         params: { | ||||
|           ...this.search, | ||||
|           departmentId: this.tabIndex === 0 ? this.search.departmentId : '', | ||||
|           tagIds: this.tabIndex === 1 ? (this.currIndex >= 0 ? this.tagsList[this.currIndex].id : '') : '', | ||||
|           listType: this.tabIndex | ||||
|         } | ||||
|       }).then(res => { | ||||
|         if (res.code == 0) { | ||||
|           this.tableData = res.data.records | ||||
|           this.total = res.data.total | ||||
|  | ||||
|           this.$nextTick(() => { | ||||
|             this.loading = false | ||||
|           }) | ||||
|         } else { | ||||
|           this.loading = false | ||||
|         } | ||||
|       }).catch(() => { | ||||
|         this.loading = false | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     removeAll() { | ||||
|       if (!this.ids) return | ||||
|  | ||||
|       if (this.tabIndex === 1) { | ||||
|         this.removeTags(this.ids) | ||||
|       } else { | ||||
|         this.remove(this.ids) | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     handleSelectionChange(e) { | ||||
|       this.ids = e.map(v => v.id).join(',') | ||||
|     }, | ||||
|  | ||||
|     filterNode(value, data) { | ||||
|       if (!value) return true | ||||
|       return data.name.indexOf(value) !== -1 | ||||
|     }, | ||||
|  | ||||
|     changeTab(id, index) { | ||||
|       this.currIndex = index | ||||
|       this.search.areaId = id | ||||
|  | ||||
|       this.$nextTick(() => { | ||||
|         this.getList() | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     getTree() { | ||||
|       this.instance.post(`/app/wxcp/wxdepartment/listAll?unitName=${this.unitName}`).then(res => { | ||||
|         if (res.code === 0) { | ||||
|           let parent = res.data.map(v => { | ||||
|             v.label = v.name | ||||
|             v.children = [] | ||||
|  | ||||
|             return v | ||||
|           }).filter(e => !e.parentid)[0] | ||||
|           this.defaultExpanded = [parent.id] | ||||
|           this.defaultChecked = [parent.id] | ||||
|           this.search.departmentId = parent.id | ||||
|           this.departmentName = parent.name | ||||
|           this.addChild(parent, res.data) | ||||
|           this.unitList = [parent] | ||||
|  | ||||
|           this.$nextTick(() => { | ||||
|             this.$refs.tree.setCurrentKey(parent.id) | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     addChild(parent, list) { | ||||
|       for (let i = 0; i < list.length; i++) { | ||||
|         if (list[i].parentid === parent.id) { | ||||
|           list[i].i = parent.children.length | ||||
|           parent.children.push(list[i]) | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (parent.children.length) { | ||||
|         parent.children.forEach(v => { | ||||
|           v.len = parent.children.length | ||||
|         }) | ||||
|       } | ||||
|  | ||||
|       if (list.length > 0) { | ||||
|         parent['children'].map(v => this.addChild(v, list)) | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     formatList(list) { | ||||
|       var arr = [] | ||||
|       for (let item of list) { | ||||
|         if (item.childrenUser && item.childrenUser.length) { | ||||
|           delete item.childrenUser | ||||
|         } | ||||
|  | ||||
|         if (item.childrenDept && item.childrenDept.length) { | ||||
|           this.formatList(item.childrenDept) | ||||
|         } | ||||
|  | ||||
|         arr.push(item) | ||||
|       } | ||||
|  | ||||
|       return arr | ||||
|     }, | ||||
|  | ||||
|     remove(id) { | ||||
|       this.$confirm('确定删除该数据?').then(() => { | ||||
|         this.instance.post(`/app/wxcp/wxuser/delete?id=${id}`).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.$message.success('删除成功!') | ||||
|             this.getList() | ||||
|           } | ||||
|         }) | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     toAdd(id) { | ||||
|       this.$emit('change', { | ||||
|         type: 'Add', | ||||
|         params: { | ||||
|           id: id || '', | ||||
|           departmentId: this.search.departmentId || '', | ||||
|           departmentName: this.departmentName || '' | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .addressBook { | ||||
|   .addressBook-table__btns { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   .table-tags { | ||||
|     .el-tag { | ||||
|       margin-right: 8px; | ||||
|       margin-bottom: 8px; | ||||
|  | ||||
|       &:last-child { | ||||
|         margin-right: 0; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .import-wrapper { | ||||
|     & > h2 { | ||||
|       margin-bottom: 8px; | ||||
|       font-size: 16px; | ||||
|       color: #222222; | ||||
|       font-weight: Bold; | ||||
|     } | ||||
|  | ||||
|     .import-wrapper__tips { | ||||
|       line-height: 1; | ||||
|       margin-bottom: 24px; | ||||
|  | ||||
|       div { | ||||
|         display: flex; | ||||
|         margin-bottom: 8px; | ||||
|         color: #222222; | ||||
|         font-size: 14px; | ||||
|  | ||||
|         span { | ||||
|           cursor: pointer; | ||||
|           color: #2266FF; | ||||
|  | ||||
|           &:hover { | ||||
|             opacity: 0.8; | ||||
|             text-decoration: underline; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .import-files { | ||||
|       i { | ||||
|         display: block; | ||||
|         margin-top: 8px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     i { | ||||
|       color: #999999; | ||||
|       font-size: 12px; | ||||
|       font-style: normal; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .tree-container { | ||||
|     position: relative; | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     justify-content: space-between; | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|  | ||||
|     .tree-name { | ||||
|       padding-right: 30px; | ||||
|     } | ||||
|  | ||||
|     i { | ||||
|       position: absolute; | ||||
|       top: 50%; | ||||
|       right: 8px; | ||||
|       transform: translateY(-50%); | ||||
|       padding-right: 8px; | ||||
|       font-weight: normal; | ||||
|       color: #fff; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .el-tag { | ||||
|     margin-right: 8px; | ||||
|     border: 1px solid #D0D4DC; | ||||
|     background: #F3F4F7; | ||||
|     border-radius: 4px; | ||||
|     font-size: 13px; | ||||
|     color: #222222; | ||||
|  | ||||
|     &:last-child { | ||||
|       margin-right: 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .table-avatar { | ||||
|     width: 40px; | ||||
|     height: 40px; | ||||
|     margin-top: 3px; | ||||
|     border-radius: 2px; | ||||
|     border: 1px solid #CCCCCC; | ||||
|   } | ||||
|  | ||||
|   .el-button--mini, .el-button--mini.is-round { | ||||
|     height: 28px; | ||||
|     line-height: 28px; | ||||
|     padding: 0; | ||||
|     font-size: 12px; | ||||
|  | ||||
|     :deep( span ){ | ||||
|       margin-left: 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .addressBook-left__list--title { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     margin: 8px 8px 0; | ||||
|  | ||||
|     .addressBook-left__list--search { | ||||
|       flex: 1; | ||||
|  | ||||
|       :deep( input ){ | ||||
|         width: 100%; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .el-button { | ||||
|       width: 84px; | ||||
|       flex-shrink: 1; | ||||
|       margin-right: 8px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .addressBook-left { | ||||
|     width: 100%; | ||||
|     height: auto; | ||||
|     background: #FAFAFB; | ||||
|  | ||||
|     .addressBook-left__title { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       width: 100%; | ||||
|       height: 40px; | ||||
|       background: #ffffff; | ||||
|  | ||||
|       h2 { | ||||
|         flex: 1; | ||||
|         height: 100%; | ||||
|         line-height: 40px; | ||||
|         color: #222; | ||||
|         font-size: 14px; | ||||
|         text-align: center; | ||||
|         cursor: pointer; | ||||
|         border-bottom: 2px solid transparent; | ||||
|  | ||||
|         &.tab-active { | ||||
|           color: #2266FF; | ||||
|           border-bottom: 2px solid #2266FF; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // ::-webkit-scrollbar { | ||||
|     //   width: 1px; | ||||
|     // } | ||||
|  | ||||
|     .addressBook-left__list--wrapper { | ||||
|       height: calc(100% - 68px); | ||||
|       padding: 8px; | ||||
|     } | ||||
|  | ||||
|     .addressBook-left__list { | ||||
|       width: 100%; | ||||
|       height: 100%; | ||||
|       overflow: auto; | ||||
|  | ||||
|       :deep( .el-tree ){ | ||||
|         width: fit-content; | ||||
|         min-width: 100%; | ||||
|       } | ||||
|  | ||||
|       :deep( .el-scrollbar__wrap ){ | ||||
|         margin-bottom: 0 !important; | ||||
|         overflow-x: hidden; | ||||
|  | ||||
|         .el-scrollbar__view { | ||||
|           width: fit-content; | ||||
|           min-width: 100%; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .addressBook-left__tags--item { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: space-between; | ||||
|         height: 40px; | ||||
|         padding: 0 8px 0 16px; | ||||
|         cursor: pointer; | ||||
|         color: #222222; | ||||
|  | ||||
|         &.addressBook-left__tags--item-active, &:hover { | ||||
|           background: #E8EFFF; | ||||
|           color: #2266FF; | ||||
|  | ||||
|           i, span { | ||||
|             color: #2266FF; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         span { | ||||
|           font-size: 14px; | ||||
|         } | ||||
|  | ||||
|         i { | ||||
|           cursor: pointer; | ||||
|           color: #8e9ebf; | ||||
|           font-size: 16px; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       span { | ||||
|         color: #222222; | ||||
|         font-size: 14px; | ||||
|       } | ||||
|  | ||||
|       :deep( .el-tree ){ | ||||
|         background: transparent; | ||||
|  | ||||
|         .el-tree-node__expand-icon.is-leaf { | ||||
|           color: transparent !important; | ||||
|         } | ||||
|  | ||||
|         .el-tree-node__content > .el-tree-node__expand-icon { | ||||
|           padding: 4px; | ||||
|         } | ||||
|  | ||||
|         .el-tree-node__content { | ||||
|           height: 32px; | ||||
|         } | ||||
|  | ||||
|         .el-tree__empty-text { | ||||
|           color: #222; | ||||
|           font-size: 14px; | ||||
|         } | ||||
|  | ||||
|         .el-tree-node__children .el-tree-node__content { | ||||
|           height: 32px; | ||||
|         } | ||||
|  | ||||
|         .el-tree-node__content:hover { | ||||
|           background: #E8EFFF; | ||||
|           color: #222222; | ||||
|           border-radius: 2px; | ||||
|         } | ||||
|  | ||||
|         .is-current > .el-tree-node__content { | ||||
|           &:hover { | ||||
|             background: #2266FF; | ||||
|             color: #fff; | ||||
|           } | ||||
|  | ||||
|           background: #2266FF; | ||||
|  | ||||
|           span { | ||||
|             color: #fff; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   :deep( .ai-list__content--right ){ | ||||
|     flex: 1; | ||||
|     min-width: 0; | ||||
|     margin-left: 1px; | ||||
|     box-shadow: none; | ||||
|  | ||||
|     .ai-list__content--right-wrapper { | ||||
|       width: 100%; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| </style> | ||||
| @@ -1,354 +0,0 @@ | ||||
| <template> | ||||
|   <ai-list class="AppPartyPayment"> | ||||
|     <template slot="title"> | ||||
|       <ai-title title="党费缴纳" isShowBottomBorder :instance="instance"></ai-title> | ||||
|     </template> | ||||
|     <template #content> | ||||
|       <div class="statistics-top"> | ||||
|         <div class="statistics-top__item"> | ||||
|           <span>本月已缴纳党费金额</span> | ||||
|           <h2 style="color: #2266FF;">{{ topTotal['本月缴纳党费'] || 0 }}</h2> | ||||
|         </div> | ||||
|         <div class="statistics-top__item"> | ||||
|           <span>本月已缴纳人数</span> | ||||
|           <h2 style="color: #22AA99;">{{ topTotal['本月已缴纳人数'] || 0  }}</h2> | ||||
|         </div> | ||||
|         <div class="statistics-top__item"> | ||||
|           <span>本月未缴纳党费金额</span> | ||||
|           <h2 style="color: #F8B425">{{ topTotal['本月未缴纳党费'] || 0 }}</h2> | ||||
|         </div> | ||||
|         <div class="statistics-top__item"> | ||||
|           <span>本月未缴纳人数</span> | ||||
|           <h2 style="color: #999;">{{ topTotal['本月未缴纳人数'] || 0 }}</h2> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="content"> | ||||
|         <ai-search-bar> | ||||
|           <template #left> | ||||
|             <ai-party :instance="instance" v-model="search.partyOrgId" :topOrgId="topOrgId" :name.sync="search.partyOrgName" | ||||
|                     style="display:inline-block" @origin="handlePartyOrgSelect" customClicker | ||||
|                     url="/app/partyOrganization/queryAllChildren"> | ||||
|               <el-input size="small" v-model="search.partyOrgName" readonly placeholder="选择党组织"></el-input> | ||||
|             </ai-party> | ||||
|             <el-date-picker v-model="search.ymd" type="month" placeholder="选择日期" size="small" value-format="yyyy-MM" @change="getListInit"></el-date-picker> | ||||
|             <ai-select | ||||
|               v-model="search.status" | ||||
|               @change="getListInit()" | ||||
|               placeholder="状态" | ||||
|               :selectList="dict.getDict('zhishengPartyFeeRecordStatus')"> | ||||
|             </ai-select> | ||||
|           </template> | ||||
|           <template #right> | ||||
|             <el-input size="small" v-model="search.name" placeholder="姓名/身份证" | ||||
|               suffix-icon="iconfont iconSearch" v-throttle="() => {getListInit()}" clearable @clear="search.name = '', getListInit()"/> | ||||
|           </template> | ||||
|         </ai-search-bar> | ||||
|         <ai-search-bar> | ||||
|           <template #left> | ||||
|             <el-button size="small" type="primary" icon="iconfont iconAdd" @click="toAdd('')">新增</el-button> | ||||
|           </template> | ||||
|           <template #right> | ||||
|             <ai-import :instance="instance" :dict="dict" type="appconvenientaddressbook" name="党费缴纳" | ||||
|               @success="getListInit()" importUrl="/app/appdfjnzhisheng/import" url="/app/appdfjnzhisheng/downloadTemplate"> | ||||
|               <el-button icon="iconfont iconImport">导入</el-button> | ||||
|             </ai-import> | ||||
|             <ai-download :instance="instance" url="/app/appdfjnzhisheng/export" :params="search" fileName="党费缴纳" | ||||
|               :disabled="tableData.length == 0"> | ||||
|               <el-button icon="iconfont iconExported" :disabled="tableData.length == 0">导出</el-button> | ||||
|             </ai-download> | ||||
|           </template> | ||||
|         </ai-search-bar> | ||||
|         <ai-table | ||||
|           :tableData="tableData" | ||||
|           :col-configs="colConfigs" | ||||
|           :total="total" | ||||
|           :current.sync="search.current" | ||||
|           :size.sync="search.size" | ||||
|           @getList="getList"> | ||||
|           <el-table-column slot="option" label="操作" align="center" width="160px" fixed="right"> | ||||
|             <template slot-scope="{ row }"> | ||||
|               <div class="table-options"> | ||||
|                 <el-button type="text" title="编辑" @click="edit(row)">编辑</el-button> | ||||
|                 <el-button type="text" title="删除" @click="remove(row)">删除</el-button> | ||||
|               </div> | ||||
|             </template> | ||||
|           </el-table-column> | ||||
|         </ai-table> | ||||
|       </div> | ||||
|       <ai-dialog :visible.sync="dialog" :title="dialogTitle" @closed="$refs.form.resetFields()" @onConfirm="handlePayment"> | ||||
|         <el-form ref="form" :rules="rules" size="small" :model="form" label-width="80px"> | ||||
|           <el-form-item label="党员姓名" prop="name"> | ||||
|             <el-autocomplete ref="poiInput" v-model="form.name" size="small" clearable :fetch-suggestions="handleSearch" | ||||
|               placeholder="请输入党员姓名" @select="handleSelect" :trigger-on-focus="false" style="width:100%;"> | ||||
|               <template slot-scope="{item}"> | ||||
|                 <span style="direction: rtl" v-text="`${item.name}-${item.partyOrgName}(${item.idNumber})`"/> | ||||
|               </template> | ||||
|             </el-autocomplete>  | ||||
|           </el-form-item> | ||||
|           <el-form-item label="身份证号" prop="idNumber"> | ||||
|             <el-input placeholder="自动带入" v-model="form.idNumber" :disabled="true" size="small"></el-input> | ||||
|           </el-form-item> | ||||
|           <el-form-item label="缴纳状态" prop="status"> | ||||
|             <el-radio-group v-model="form.status"> | ||||
|               <el-radio label="1">已缴纳</el-radio> | ||||
|               <el-radio label="0">未缴纳</el-radio> | ||||
|             </el-radio-group> | ||||
|           </el-form-item> | ||||
|           <el-form-item label="缴纳月份" prop="ymd"> | ||||
|             <el-date-picker v-model="form.ymd" type="month" placeholder="选择年月" size="small" value-format="yyyy-MM"></el-date-picker> | ||||
|           </el-form-item> | ||||
|           <el-form-item label="金额(元)" prop="amount"> | ||||
|             <el-input-number :precision="2" size="small" type="input" v-model="form.amount" clearable placeholder="2位小数"></el-input-number> | ||||
|           </el-form-item> | ||||
|         </el-form> | ||||
|       </ai-dialog> | ||||
|     </template> | ||||
|   </ai-list> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import {mapState} from 'vuex' | ||||
|   export default { | ||||
|     name: 'AppPartyPayment', | ||||
|     label: '党费缴纳', | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object | ||||
|     }, | ||||
|     data () { | ||||
|       return { | ||||
|         search: { | ||||
|           current: 1, | ||||
|           size: 10, | ||||
|           partyOrgId: '', | ||||
|           partyOrgName: '', | ||||
|           name: '', | ||||
|           status: '', | ||||
|           ymd: '' | ||||
|         }, | ||||
|         total: 0, | ||||
|         tableData: [], | ||||
|         colConfigs: [ | ||||
|           { prop: 'orgName', label: '党组织' }, | ||||
|           { prop: 'name', label: '党员姓名', align: 'center',  }, | ||||
|           { prop: 'idNumber', label: '身份证', align: 'center' }, | ||||
|           { prop: 'ymd', label: '缴纳月份', align: 'center' }, | ||||
|           { prop: 'amount', label: '缴纳党费', align: 'center' }, | ||||
|           { prop: 'status', label: '状态', align: 'center',format: v => this.dict.getLabel('zhishengPartyFeeRecordStatus', v)}, | ||||
|           { slot: 'option'} | ||||
|         ], | ||||
|         topOrgId: '', | ||||
|         dialog: false, | ||||
|         dialogTitle: '', | ||||
|         form: { | ||||
|           name: '', | ||||
|           idNumber: '', | ||||
|           status: '0', | ||||
|           ymd: '', | ||||
|           amount: '', | ||||
|           orgId: '', | ||||
|           orgName: '', | ||||
|           partyId: '' | ||||
|         }, | ||||
|         topTotal: {} | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|       params () { | ||||
|         return { | ||||
|           ...this.search, | ||||
|           startTime: this.search.type === '3' ? this.date[0] : '', | ||||
|           endTime: this.search.type === '3' ? this.date[1] : '' | ||||
|         } | ||||
|       }, | ||||
|       rules() { | ||||
|         return { | ||||
|           name: [{ required: true, message: '请输入党员姓名', trigger: 'change'}], | ||||
|           idNumber: [{ required: true, message: '请选择党员', trigger: 'change'}], | ||||
|           status: [{ required: true, message: '请选择缴费状态', trigger: 'change'}], | ||||
|           ymd: [{ required: true, message: '请选择缴纳月份', trigger: 'change'}], | ||||
|           amount: [{ required: true, message: '请输入金额', trigger: 'blur'}], | ||||
|         } | ||||
|       }, | ||||
|       ...mapState(['user']), | ||||
|     }, | ||||
|  | ||||
|     created () { | ||||
|       this.topOrgId = this.user.info.organizationId | ||||
|       this.dict.load('zhishengPartyFeeRecordStatus').then(() => { | ||||
|         this.getList() | ||||
|       }) | ||||
|       this.getTotal() | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       toAdd() { | ||||
|         this.form =  { | ||||
|           name: '', | ||||
|           idNumber: '', | ||||
|           status: '0', | ||||
|           ymd: '', | ||||
|           amount: '', | ||||
|           orgId: '', | ||||
|           orgName: '', | ||||
|           partyId: '' | ||||
|         } | ||||
|         this.dialogTitle = '新增党费信息' | ||||
|         this.dialog = true | ||||
|       }, | ||||
|       edit(row) { | ||||
|         this.form = row | ||||
|         this.dialog = true | ||||
|         this.dialogTitle = '编辑党费信息' | ||||
|       }, | ||||
|       handleSearch(e, cb) { | ||||
|         this.instance.post(`/app/appparty/fuzzyList?name=${e}&size=50`).then((res) => { | ||||
|           if (res.code == 0) { | ||||
|             cb(res.data) | ||||
|           } | ||||
|         }); | ||||
|       }, | ||||
|       handleSelect(e) { | ||||
|         this.form.name = e.name | ||||
|         this.form.idNumber = e.idNumber | ||||
|         this.form.orgId = e.partyOrgId | ||||
|         this.form.orgName = e.partyOrgName | ||||
|         this.form.partyId = e.id | ||||
|       }, | ||||
|       handlePayment() { | ||||
|         this.$refs.form.validate((valid) => { | ||||
|           if (valid) { | ||||
|             this.instance.post(`/app/appdfjnzhisheng/addOrUpdate`, {...this.form}).then((res) => { | ||||
|               if (res.code == 0) { | ||||
|                 this.$message.success(this.dialogTitle == '编辑党费信息' ? "编辑成功" : "新增成功"); | ||||
|                 this.$refs.form.resetFields() | ||||
|                 this.dialog = false | ||||
|                 this.getListInit() | ||||
|                 this.getTotal() | ||||
|               } | ||||
|             }); | ||||
|           } | ||||
|         }); | ||||
|       }, | ||||
|       handlePartyOrgSelect(e) { | ||||
|         let {isLeaf, name, id} = e?.[0] || {}; | ||||
|         if (isLeaf == 1) { | ||||
|           this.search.partyOrgName = name; | ||||
|           this.search.partyOrgId = id; | ||||
|         } | ||||
|         this.getListInit() | ||||
|       }, | ||||
|       getListInit() { | ||||
|         this.search.current = 1 | ||||
|         this.getList() | ||||
|       }, | ||||
|       getList () { | ||||
|         this.instance.post(`/app/appdfjnzhisheng/list`, null, { | ||||
|           params: { | ||||
|             ...this.search, | ||||
|             orgId: this.search.partyOrgId | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             res.data.records.map((item) => { | ||||
|               item.ymd = item.ymd.substring(0, 7) | ||||
|             }) | ||||
|             this.tableData = res.data.records | ||||
|             this.total = res.data.total | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|       remove(row) { | ||||
|         this.$confirm('确定删除该数据?').then(() => { | ||||
|           this.instance.post(`/app/appdfjnzhisheng/delete?ids=${row.id}`).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               this.$message.success('删除成功!') | ||||
|               this.getList() | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       }, | ||||
|       getTotal() { | ||||
|         this.instance.post(`/app/appdfjnzhisheng/statistics`).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.topTotal = res.data | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .AppPartyPayment { | ||||
|  | ||||
|     .bottom { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|  | ||||
|       & > .ai-card { | ||||
|         flex: 1; | ||||
|  | ||||
|         &:last-child { | ||||
|           margin-left: 20px; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     :deep( .ai-list__content ){ | ||||
|       padding: 0!important; | ||||
|  | ||||
|       .ai-list__content--right-wrapper { | ||||
|         background: transparent!important; | ||||
|         box-shadow: none!important; | ||||
|         margin: 0!important; | ||||
|         padding: 12px 16px 12px!important; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     :deep( .ai-card) { | ||||
|       .ai-card__body { | ||||
|         padding: 12px 16px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .statistics-top { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       margin-bottom: 20px; | ||||
|  | ||||
|       & > div { | ||||
|         flex: 1; | ||||
|         height: 96px; | ||||
|         line-height: 1; | ||||
|         margin-right: 20px; | ||||
|         padding: 16px 24px; | ||||
|         background: #FFFFFF; | ||||
|         box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.15); | ||||
|         border-radius: 4px; | ||||
|  | ||||
|         &:last-child { | ||||
|           margin-right: 0; | ||||
|         } | ||||
|  | ||||
|         h3 { | ||||
|           font-size: 24px; | ||||
|         } | ||||
|  | ||||
|         span { | ||||
|           display: block; | ||||
|           margin-bottom: 16px; | ||||
|           color: #888888; | ||||
|           font-size: 16px; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .content { | ||||
|       padding: 16px; | ||||
|       background: #FFFFFF; | ||||
|       box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.15); | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
| @@ -1,35 +0,0 @@ | ||||
| <template> | ||||
|   <section class="AppPartyScore"> | ||||
|     <component :is="currentPage" v-bind="$props"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import PsList from "./psList"; | ||||
| import PsDetail from "./psDetail"; | ||||
|  | ||||
| export default { | ||||
|   name: "AppPartyScore", | ||||
|   components: {PsDetail, PsList}, | ||||
|   label: "党员积分", | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function | ||||
|   }, | ||||
|   computed: { | ||||
|     currentPage() { | ||||
|       return this.$route.query.id ? PsDetail : PsList | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.dict.load("partyIntegralType") | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AppPartyScore { | ||||
|   height: 100%; | ||||
| } | ||||
| </style> | ||||
| @@ -1,154 +0,0 @@ | ||||
| <template> | ||||
|   <section class="psDetail"> | ||||
|     <ai-detail> | ||||
|       <ai-title slot="title" title="积分详情" isShowBottomBorder isShowBack @onBackClick="back"/> | ||||
|       <template #content> | ||||
|         <el-row type="flex"> | ||||
|           <ai-card hideTitle class="staCard fill"> | ||||
|             <template #content> | ||||
|               <div class="color-999" v-text="`姓名`"/> | ||||
|               <b v-text="detail.name"/> | ||||
|             </template> | ||||
|           </ai-card> | ||||
|           <ai-card hideTitle class="staCard fill"> | ||||
|             <template #content> | ||||
|               <div class="color-999" v-text="`学习强国`"/> | ||||
|               <el-button type="text" @click="handleEditLearningIntergral(detail.id)">编辑</el-button> | ||||
|               <b class="color-26f" v-text="detail.learningIntegral||0"/> | ||||
|             </template> | ||||
|           </ai-card> | ||||
|           <ai-card hideTitle class="staCard fill"> | ||||
|             <template slot="content"> | ||||
|               <div class="color-999" v-text="`个人积分`"/> | ||||
|               <b class="color-26f" v-text="detail.integral||0"/> | ||||
|             </template> | ||||
|           </ai-card> | ||||
|           <ai-card hideTitle class="staCard fill"> | ||||
|             <template #content> | ||||
|               <div class="color-999" v-text="`家庭积分`"/> | ||||
|               <b class="color-26f" v-text="detail.familySurplusIntegral||0"/> | ||||
|             </template> | ||||
|           </ai-card> | ||||
|         </el-row> | ||||
|         <ai-card title="余额变动明细"> | ||||
|           <template #content> | ||||
|             <ai-table :tableData="detail.integralInfoList" :isShowPagination="false" :col-configs="colConfigs" | ||||
|                       :dict="dict"/> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|       </template> | ||||
|     </ai-detail> | ||||
|     <ai-dialog :visible.sync="dialog" title="学习强国设置" width="500px" @close="form={}" @onConfirm="submit"> | ||||
|       <el-form :model="form" size="small" ref="DialogForm" :rules="rules" label-width="110px"> | ||||
|         <el-form-item label="学习强国积分" prop="learningIntegral"> | ||||
|           <el-input v-model.number="form.learningIntegral" placeholder="请输入正整数" clearable/> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|     </ai-dialog> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "psDetail", | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       detail: {}, | ||||
|       colConfigs: [ | ||||
|         {label: "时间", prop: "createTime"}, | ||||
|         {label: "类型", prop: "integralType", align: 'center', dict: "partyIntegralType"}, | ||||
|         {label: "变动积分", prop: "integral", align: 'center'}, | ||||
|         {label: "剩余积分", prop: "residualIntegral", align: 'center'}, | ||||
|         {label: "调整说明", prop: "remark"}, | ||||
|       ], | ||||
|       dialog: false, | ||||
|       form: {}, | ||||
|       rules: { | ||||
|         learningIntegral: [ | ||||
|           {required: true, message: "请输入学习强国积分"}, | ||||
|           {pattern: /^\d+$/g, message: "请输入正整数"} | ||||
|         ] | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getDetail() { | ||||
|       let {id} = this.$route.query | ||||
|       this.instance.post("/app/appparty/getPartyIntegralDetail", null, { | ||||
|         params: {id} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.detail = res.data | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     back() { | ||||
|       this.$router.push({}) | ||||
|     }, | ||||
|     submit() { | ||||
|       this.$refs.DialogForm.validate(v => { | ||||
|         if (v) { | ||||
|           this.instance.post("/app/appparty/editLearningIntegral", null,{ | ||||
|             params:this.form | ||||
|           }).then(res => { | ||||
|             if (res?.code == 0) { | ||||
|               this.$message.success("提交成功!") | ||||
|               this.dialog = false | ||||
|               this.getDetail() | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleEditLearningIntergral(partyMemberId) { | ||||
|       this.dialog = true | ||||
|       this.form = {partyMemberId} | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.getDetail() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .psDetail { | ||||
|   height: 100%; | ||||
|  | ||||
|   .color-999 { | ||||
|     color: #999; | ||||
|   } | ||||
|  | ||||
|   .color-26f { | ||||
|     color: #26f; | ||||
|   } | ||||
|  | ||||
|   :deep(.staCard ){ | ||||
|     font-size: 14px; | ||||
|  | ||||
|     b { | ||||
|       font-size: 24px; | ||||
|       line-height: 40px; | ||||
|     } | ||||
|  | ||||
|     & + .staCard { | ||||
|       margin-left: 16px; | ||||
|     } | ||||
|  | ||||
|     .ai-card__body { | ||||
|       position: relative; | ||||
|  | ||||
|       .el-button { | ||||
|         position: absolute; | ||||
|         right: 32px; | ||||
|         top: 6px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -1,116 +0,0 @@ | ||||
| <template> | ||||
|   <section class="psList"> | ||||
|     <ai-list> | ||||
|       <ai-title slot="title" title="党员积分" isShowBottomBorder/> | ||||
|       <template #content> | ||||
|         <ai-search-bar> | ||||
|           <template #right> | ||||
|             <ai-import :instance="instance" name="党员积分" title="导入党员积分" | ||||
|                        suffixName="xlsx" | ||||
|                        url="/app/apppartyintegralinfo/downloadTemplate" | ||||
|                        importUrl="/app/apppartyintegralinfo/import" | ||||
|                        @onSuccess="page.current=1,getTableData()"/> | ||||
|           </template> | ||||
|         </ai-search-bar> | ||||
|         <ai-table :tableData="tableData" :total="page.total" :current.sync="page.current" :size.sync="page.size" | ||||
|                   @getList="getTableData" :col-configs="colConfigs" :dict="dict"> | ||||
|           <el-table-column slot="options" label="操作" fixed="right" align="center"> | ||||
|             <template slot-scope="{row}"> | ||||
|               <el-button type="text" @click="getFamilyByPartyId(row.idNumber)">家庭成员</el-button> | ||||
|               <el-button type="text" @click="showDetail(row.id)">详情</el-button> | ||||
|             </template> | ||||
|           </el-table-column> | ||||
|         </ai-table> | ||||
|       </template> | ||||
|     </ai-list> | ||||
|     <ai-dialog :visible.sync="dialog" title="家庭成员" :customFooter="true" width="780px" @close="familyList=[]"> | ||||
|       <ai-table :tableData="familyList" :isShowPagination="false" :col-configs="familyCols" :dict="dict"/> | ||||
|       <div class="dialog-footer" slot="footer"> | ||||
|         <el-button @click="dialog=false">关 闭</el-button> | ||||
|       </div> | ||||
|     </ai-dialog> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapState} from "vuex"; | ||||
| import {ID} from "dui/lib/js/utils"; | ||||
|  | ||||
| export default { | ||||
|   name: "psList", | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|     colConfigs() { | ||||
|       return [ | ||||
|         {label: "姓名", prop: "name"}, | ||||
|         {label: "个人积分", prop: "integral", align: "center"}, | ||||
|         {label: "家庭积分", prop: "familySurplusIntegral", align: "center"}, | ||||
|         {label: "学习强国", prop: "learningIntegral", align: "center"}, | ||||
|         {slot: "options"} | ||||
|       ] | ||||
|     }, | ||||
|     familyCols() { | ||||
|       return [ | ||||
|         { | ||||
|           label: '与户主关系', prop: 'householdRelation', align: 'center', width: 165, | ||||
|           render: (h, {row}) => h('p', this.dict.getLabel('householdRelation', row.householdRelation || "户主")) | ||||
|         }, | ||||
|         {label: '类型', prop: 'residentType', align: 'center', dict: "residentType"}, | ||||
|         {label: '姓名', prop: 'name', align: 'center'}, | ||||
|         {label: '身份证号', render: (h, {row}) => h('p', ID.hideId(row.idNumber)), width: 165}, | ||||
|         {label: '联系电话', prop: 'phone', align: 'center', width: 120} | ||||
|       ] | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       search: {}, | ||||
|       page: {current: 1, size: 10, total: 0}, | ||||
|       tableData: [], | ||||
|       dialog: false, | ||||
|       familyList: [], | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     back() { | ||||
|       this.$router.push({}) | ||||
|     }, | ||||
|     getTableData() { | ||||
|       this.instance.post("/app/appparty/listByPartyIntegral", null, { | ||||
|         params: {...this.page, ...this.search} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.tableData = res.data?.records | ||||
|           this.page.total = res.data.total | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     showDetail(id) { | ||||
|       this.$router.push({query: {id}}) | ||||
|     }, | ||||
|     getFamilyByPartyId(idNumber) { | ||||
|       this.instance.post("/app/appresident/queryHomeMember", null, { | ||||
|         params: {idNumber} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.familyList = res.data?.family || [] | ||||
|           this.dialog = true | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.getTableData() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .psList { | ||||
|   height: 100%; | ||||
| } | ||||
| </style> | ||||
| @@ -1,34 +0,0 @@ | ||||
| <template> | ||||
|   <section class="AppPartyScoreFlow"> | ||||
|     <component :is="currentPage" v-bind="$props"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import PsfList from "./psfList"; | ||||
|  | ||||
| export default { | ||||
|   name: "AppPartyScoreFlow", | ||||
|   components: {PsfList}, | ||||
|   label: "党员积分明细", | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function | ||||
|   }, | ||||
|   computed: { | ||||
|     currentPage() { | ||||
|       return PsfList | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.dict.load("partyIntegralType") | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AppPartyScoreFlow { | ||||
|   height: 100%; | ||||
| } | ||||
| </style> | ||||
| @@ -1,150 +0,0 @@ | ||||
| <template> | ||||
|   <section class="psfList"> | ||||
|     <ai-list> | ||||
|       <ai-title slot="title" title="党员积分明细" isShowBottomBorder/> | ||||
|       <template #content> | ||||
|         <ai-search-bar> | ||||
|           <template #left> | ||||
|             <el-button type="primary" icon="iconfont iconAdd" @click="dialog=true">添加</el-button> | ||||
|             <el-date-picker type="daterange" placeholder="日期" size="small" clearable v-model="createTime" | ||||
|                             @change="handleSearchTime" start-placeholder="开始时间" end-placeholder="结束时间" | ||||
|                             value-format="yyyy-MM-dd HH:mm:ss" :default-time="['00:00:00','23:59:59']"/> | ||||
|           </template> | ||||
|           <template #right> | ||||
|             <el-input size="small" placeholder="搜索党员" v-model="search.partyName" clearable | ||||
|                       @change="page.current=1,getTableData()" suffix-icon="iconfont iconSearch"/> | ||||
|           </template> | ||||
|         </ai-search-bar> | ||||
|         <ai-table :tableData="tableData" :total="page.total" :current.sync="page.current" :size.sync="page.size" | ||||
|                   @getList="getTableData" :col-configs="colConfigs" :dict="dict"> | ||||
|           <el-table-column slot="options" label="操作" fixed="right" align="center"> | ||||
|             <template slot-scope="{row}"> | ||||
|               <el-button type="text" @click="showDetail(row)">详情</el-button> | ||||
|             </template> | ||||
|           </el-table-column> | ||||
|         </ai-table> | ||||
|       </template> | ||||
|     </ai-list> | ||||
|     <ai-dialog :visible.sync="dialog" title="积分对象" width="600px" @close="form={}" @onConfirm="submit" | ||||
|                :customFooter="!isAdd"> | ||||
|       <el-form v-if="isAdd" :model="form" size="small" ref="DialogForm" :rules="rules" label-width="80px"> | ||||
|         <el-form-item label="选择人员" prop="partyId"> | ||||
|           <ai-select v-model="form.partyId" action="/app/appparty/list" :instance="instance" | ||||
|                      :prop="{label:'name'}"/> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="调整说明" prop="remark"> | ||||
|           <el-input type="textarea" placeholder="请输入" v-model="form.remark" maxlength="100" show-word-limit rows="3" | ||||
|                     clearable/> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="类型" prop="integralType"> | ||||
|           <ai-select v-model="form.integralType" :selectList="dict.getDict('partyIntegralType')"/> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="积分" prop="integral"> | ||||
|           <el-input v-model.number="form.integral" placeholder="请输入正整数" clearable/> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|       <ai-wrapper v-else> | ||||
|         <ai-info-item label="对象" :value="form.partyName"/> | ||||
|         <ai-info-item label="调整说明" :value="form.remark" isLine/> | ||||
|         <ai-info-item label="类型" :value="dict.getLabel('partyIntegralType',form.integralType)"/> | ||||
|         <ai-info-item label="积分" :value="form.integral"/> | ||||
|       </ai-wrapper> | ||||
|     </ai-dialog> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapState} from "vuex"; | ||||
|  | ||||
| export default { | ||||
|   name: "psfList", | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|     colConfigs() { | ||||
|       return [ | ||||
|         {label: "对象", prop: "partyName"}, | ||||
|         {label: "调整说明", prop: "remark", align: "center"}, | ||||
|         {label: "时间", prop: "createTime"}, | ||||
|         {label: "类型", prop: "integralType", align: "center", dict: "partyIntegralType"}, | ||||
|         {label: "积分", prop: "integral", align: "center"}, | ||||
|         {slot: "options"} | ||||
|       ] | ||||
|     }, | ||||
|     isAdd() { | ||||
|       return !this.form.id | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       search: {}, | ||||
|       page: {current: 1, size: 10, total: 0}, | ||||
|       tableData: [], | ||||
|       dialog: false, | ||||
|       form: {}, | ||||
|       rules: { | ||||
|         partyId: {required: true, message: "请选择人员"}, | ||||
|         remark: {required: true, message: "请输入调整说明"}, | ||||
|         integralType: {required: true, message: "请选择类型"}, | ||||
|         integral: [ | ||||
|           {required: true, message: "请输入分数"}, | ||||
|           {pattern: /^\d+$/g, message: "请输入正整数"} | ||||
|         ], | ||||
|       }, | ||||
|       createTime: "" | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getTableData() { | ||||
|       this.instance.post("/app/apppartyintegralinfo/list", null, { | ||||
|         params: {...this.page, ...this.search} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.tableData = res.data?.records?.map(e => ({ | ||||
|             ...e, | ||||
|             integral: (e.integralType == 0 ? "-" : '+') + e.integral | ||||
|           })) | ||||
|           this.page.total = res.data.total | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     showDetail(row) { | ||||
|       this.form = JSON.parse(JSON.stringify(row)) | ||||
|       this.dialog = true | ||||
|     }, | ||||
|     submit() { | ||||
|       this.$refs.DialogForm.validate(v => { | ||||
|         if (v) { | ||||
|           let loading = this.$loading({text: "提交中..."}) | ||||
|           this.instance.post("/app/apppartyintegralinfo/addOrUpdate", this.form).then(res => { | ||||
|             loading.close() | ||||
|             if (res?.code == 0) { | ||||
|               this.$message.success("提交成功!") | ||||
|               this.dialog = false | ||||
|               this.getTableData() | ||||
|             } | ||||
|           }).catch(() => loading.close()) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleSearchTime(v) { | ||||
|       this.page.current = 1 | ||||
|       this.search.startTime = v?.[0] | ||||
|       this.search.endTime = v?.[1] | ||||
|       this.getTableData() | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.getTableData() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .psfList { | ||||
|   height: 100%; | ||||
| } | ||||
| </style> | ||||
| @@ -1,101 +0,0 @@ | ||||
| <template> | ||||
|   <section class="conference"> | ||||
|     <ai-list v-if="!showDetail"> | ||||
|       <template slot="title"> | ||||
|         <ai-title title="会议管理"></ai-title> | ||||
|       </template> | ||||
|       <template slot="tabs"> | ||||
|         <el-tabs v-model="currIndex" @tab-click="handleClick"> | ||||
|           <el-tab-pane v-for="(tab,i) in tabs" :key="i" :label="tab.label" :name="String(i)"> | ||||
|             <component :is="tab.comp" v-if="currIndex==i" :ref="currIndex" :instance="instance" :dict="dict" | ||||
|                        :permissions="permissions" @goPage="goPage" :listType="listType" /> | ||||
|           </el-tab-pane> | ||||
|         </el-tabs> | ||||
|       </template> | ||||
|     </ai-list> | ||||
|  | ||||
|     <component v-else :is="currentComp" :instance="instance" :dict="dict" :detail="detailRow" :listType="listType" @gotoEdit="gotoAdd" ></component> | ||||
|  | ||||
|   </section> | ||||
| </template> | ||||
| <script> | ||||
| import addMeeting from './addMeeting'; | ||||
| import detail from './detail' | ||||
| import list from './list' | ||||
|  | ||||
| export default { | ||||
|   name: 'AppConference', | ||||
|   label: "会议管理", | ||||
|   components: {addMeeting, detail, list}, | ||||
|   provide() { | ||||
|     return { | ||||
|       top: this | ||||
|     } | ||||
|   }, | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       //会议状态,0、草稿;1、未开始;2、进行中;3、已取消;4、已结束 | ||||
|       //参会状态,0、未确认;1、已确认;2、缺席 | ||||
|       currIndex: "0", | ||||
|       currentComp: "", | ||||
|       showDetail: false, | ||||
|       detailRow: {}, | ||||
|       listType: '1', | ||||
|     } | ||||
|   }, | ||||
|   computed:{ | ||||
|     tabs() { | ||||
|       return [ | ||||
|         {label: "我参与的会议", name: "addMeeting", comp: list, detail: detail, permission: ""}, | ||||
|         {label: "我发起的会议", name: "addMeeting",  comp: list, detail: detail, permission: ""}, | ||||
|       ] | ||||
|     }, | ||||
|   }, | ||||
|  | ||||
|   methods: { | ||||
|     goPage(params) { | ||||
|       this.detailRow = params.row | ||||
|       this.currentComp = params.comp | ||||
|  | ||||
|       if(params.comp == 'detail' || params.comp == 'addMeeting') { | ||||
|         this.showDetail = true | ||||
|       } | ||||
|     }, | ||||
|     handleClick() { | ||||
|       if (this.currIndex == 0) { | ||||
|         this.listType = '1' | ||||
|       } else { | ||||
|         this.listType = '0' | ||||
|       } | ||||
|     }, | ||||
|     goBack() { | ||||
|       this.showDetail = false; | ||||
|       if (this.currIndex == '0') { | ||||
|         this.listType = '1' | ||||
|       } else { | ||||
|         this.listType = '0' | ||||
|       } | ||||
|     }, | ||||
|     gotoAdd(obj) { | ||||
|       this.showDetail = true | ||||
|       this.detailRow = obj | ||||
|       this.currentComp = 'addMeeting' | ||||
|     }, | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
|   .conference { | ||||
|     height: 100%; | ||||
|  | ||||
|     :deep( .ai-list__content--right-wrapper ){ | ||||
|       background-color: transparent !important; | ||||
|       box-shadow: none !important; | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
| @@ -1,244 +0,0 @@ | ||||
| <template> | ||||
|   <ai-detail class="addMeeting"> | ||||
|     <ai-title slot="title" title="发起会议" isShowBack isShowBottomBorder @onBackClick="$parent.goBack"/> | ||||
|     <template #content> | ||||
|       <ai-card title="会议信息"> | ||||
|         <template #content> | ||||
|           <el-form :model="saveData" status-icon ref="ruleForm" :rules="rules" label-width="100px" | ||||
|                    label-position="right"> | ||||
|             <el-form-item label="会议标题:" prop="title"> | ||||
|               <el-input v-model="saveData.title" size="small" placeholder="请输入..." | ||||
|                         :maxlength="30" show-word-limit></el-input> | ||||
|             </el-form-item> | ||||
|             <el-row type="flex" justify="space-between"> | ||||
|               <el-form-item label="开始时间:" prop="startTime"> | ||||
|                 <el-date-picker | ||||
|                     :editable="false" | ||||
|                     value-format="yyyy-MM-dd HH:mm:ss" | ||||
|                     v-model="saveData.startTime" | ||||
|                     :picker-options="pickerOptions" | ||||
|                     type="datetime" | ||||
|                     size="small" | ||||
|                     placeholder="选择开始时间"> | ||||
|                 </el-date-picker> | ||||
|               </el-form-item> | ||||
|               <el-form-item label="结束时间:" prop="endTime"> | ||||
|                 <el-date-picker | ||||
|                     :editable="false" | ||||
|                     value-format="yyyy-MM-dd HH:mm:ss" | ||||
|                     v-model="saveData.endTime" | ||||
|                     :picker-options="pickerOptions" | ||||
|                     type="datetime" | ||||
|                     :disabled="!Boolean(saveData.startTime)" | ||||
|                     size="small" | ||||
|                     placeholder="选择结束时间"> | ||||
|                 </el-date-picker> | ||||
|               </el-form-item> | ||||
|             </el-row> | ||||
|             <el-form-item label="会议地点:" prop="address"> | ||||
|               <el-input v-model="saveData.address" size="small" placeholder="请输入..." | ||||
|                         :maxlength="30" show-word-limit></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="会议内容:" prop="content"> | ||||
|               <el-input v-model="saveData.content" size="small" placeholder="请输入..." | ||||
|                         type="textarea" :rows="8" :maxlength="500" show-word-limit></el-input> | ||||
|             </el-form-item> | ||||
|             <el-row type="flex" justify="space-between"> | ||||
|               <el-form-item label="参会提醒:" prop="noticeBefore"> | ||||
|                 <ai-select v-model="saveData.noticeBefore" | ||||
|                            placeholder="请选择" | ||||
|                            :selectList="dict.getDict('meetingNoticeBefore')" | ||||
|                 ></ai-select> | ||||
|               </el-form-item> | ||||
|               <el-form-item label="确认提醒:" prop="noticeAfter"> | ||||
|                 <ai-select v-model="saveData.noticeAfter" | ||||
|                            placeholder="请选择" | ||||
|                            :selectList="dict.getDict('meetingNoticeAfter')" | ||||
|                 ></ai-select> | ||||
|               </el-form-item> | ||||
|             </el-row> | ||||
|             <el-form-item label="会议资料:" prop="files"> | ||||
|               <ai-uploader :instance="instance" @change="handleChange" isShowTip fileType="file" | ||||
|                            v-model="saveData.fileList" | ||||
|                            acceptType=".zip,.rar,.doc,.docx,.xls,.ppt,.pptx,.pdf,.txt,.jpg,.png,.xlsx" | ||||
|                            :limit="9"></ai-uploader> | ||||
|             </el-form-item> | ||||
|           </el-form> | ||||
|         </template> | ||||
|       </ai-card> | ||||
|       <ai-card title="参会人员信息"> | ||||
|         <template #right> | ||||
|           <ai-wechat-selecter slot="append" :instance="instance" v-model="saveData.attendees"> | ||||
|             <el-button type="text" icon="iconfont iconAdd">选择参会人员</el-button> | ||||
|           </ai-wechat-selecter> | ||||
|         </template> | ||||
|         <template #content> | ||||
|           <ai-table | ||||
|               border | ||||
|               :tableData="saveData.attendees" | ||||
|               :colConfigs="colConfigs" | ||||
|               :isShowPagination="false"> | ||||
|             <el-table-column label="操作" slot="option" align="center"> | ||||
|               <template v-slot="{row}"> | ||||
|                 <el-button type="text" title="删除" @click="deletePer(row)">删除</el-button> | ||||
|               </template> | ||||
|             </el-table-column> | ||||
|           </ai-table> | ||||
|         </template> | ||||
|       </ai-card> | ||||
|     </template> | ||||
|     <template #footer> | ||||
|       <el-button @click="$parent.goBack">取消</el-button> | ||||
|       <el-button type="primary" @click="saveFrom(0)">保存草稿</el-button> | ||||
|       <el-button type="primary" @click="saveFrom(1)">保存并发布</el-button> | ||||
|     </template> | ||||
|   </ai-detail> | ||||
| </template> | ||||
| <script> | ||||
| import {mapState} from "vuex"; | ||||
| import moment from 'dayjs' | ||||
|  | ||||
| export default { | ||||
|   name: "addMeeting", | ||||
|   inject: ['top'], | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function, | ||||
|     detail: Object | ||||
|   }, | ||||
|   data() { | ||||
|     let endTimePass = (rule, value, callback) => { | ||||
|       if (value) { | ||||
|         if (moment(value).unix() - moment(this.saveData.startTime).unix() > 0) { | ||||
|           callback() | ||||
|         } else { | ||||
|           callback(new Error('结束时间要大于开始时间')); | ||||
|         } | ||||
|       } else { | ||||
|         callback(new Error('请选择结束时间')); | ||||
|       } | ||||
|     } | ||||
|     return { | ||||
|       saveData: { | ||||
|         noticeBefore: '4', | ||||
|         noticeAfter: '0', | ||||
|         attendees: [], | ||||
|         fileList: [] | ||||
|       }, | ||||
|       rules: { | ||||
|         title: [{required: true, message: '请填写会议标题', trigger: 'blur'}], | ||||
|         startTime: [{required: true, message: '请选择开始时间', trigger: 'blur'}], | ||||
|         endTime: [{required: true, validator: endTimePass, trigger: 'change'}], | ||||
|         address: [{required: true, message: '请填写会议地点', trigger: 'blur'}], | ||||
|         // content: [{required: true, message: '请填写会议内容', trigger: 'blur'}], | ||||
|         noticeBefore: [{required: true, message: '请选择参会提醒', trigger: 'blur'}], | ||||
|         noticeAfter: [{required: true, message: '请选择确认提醒', trigger: 'blur'}], | ||||
|       }, | ||||
|       pickerOptions: { | ||||
|         disabledDate(time) { | ||||
|           return time.getTime() < Date.now() - 8.64e7; | ||||
|         } | ||||
|       }, | ||||
|       showEdit: false, | ||||
|       total: 0, | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     handleChange(e) { | ||||
|       this.saveData.fileList = e | ||||
|     }, | ||||
|     deletePer(scope) { | ||||
|       this.$confirm('确认删除此参会人?') | ||||
|       .then(_ => { | ||||
|         if (this.detail.id) { //编辑 | ||||
|  | ||||
|         } else { //新增 | ||||
|           this.saveData.attendees.map((item, index) => { | ||||
|             if (item.id == scope.id) { | ||||
|               this.saveData.attendees.splice(index, 1) | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     saveFrom(status) { | ||||
|       this.$refs["ruleForm"].validate((valid) => { | ||||
|         if (this.saveData.attendees.length == 0) { | ||||
|           return this.$message.error("参会人不能为空!") | ||||
|         } | ||||
|         if (moment(this.saveData.startTime).unix() - moment(Date.now()).unix() < 0) { | ||||
|           return this.$message.error("会议开始时间已过期,请重新选择!"); | ||||
|         } | ||||
|         if (valid) { | ||||
|  | ||||
|           this.saveData.files = [] | ||||
|           this.saveData.fileList.map((item) => { | ||||
|             this.saveData.files.push(item.id) | ||||
|           }) | ||||
|           this.instance.post(`/app/appmeetinginfo/add-update`, { | ||||
|             ...this.saveData, | ||||
|             status: status, | ||||
|           }).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               if (status != 1) { | ||||
|                 this.$message.success("保存草稿成功") | ||||
|               } else { | ||||
|                 this.$message.success("发布成功") | ||||
|               } | ||||
|               this.$parent.goBack(); | ||||
|             } | ||||
|           }); | ||||
|         } | ||||
|       }); | ||||
|     }, | ||||
|     getDetail() { | ||||
|       this.instance.post(`/app/appmeetinginfo/info-id?id=${this.detail.id}`).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.saveData = { | ||||
|             ...res.data, | ||||
|           }; | ||||
|           this.saveData.fileList = res.data.files || [] | ||||
|         } | ||||
|       }); | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.dict.load("meetingNoticeAfter", "meetingNoticeBefore").then( | ||||
|         this.$nextTick(() => { | ||||
|           if (JSON.stringify(this.detail) == '{}') { | ||||
|             this.showEdit = false; | ||||
|           } else { | ||||
|             this.showEdit = true; | ||||
|             // this.saveData = {...this.detail}; | ||||
|             // this.compereList = this.saveData.hosts; | ||||
|             // this.saveData.attendees = this.saveData.attendees || []; | ||||
|             this.getDetail() | ||||
|           } | ||||
|         }) | ||||
|     ) | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|     headerTitle() { | ||||
|       return this.showEdit ? '修改会议' : '发起会议' | ||||
|     }, | ||||
|     colConfigs() { | ||||
|       return [ | ||||
|         {prop: 'name', align: 'center', label: '姓名'}, | ||||
|         {prop: 'departName', align: 'center', label: '所属单位'}, | ||||
|         {slot: 'option'} | ||||
|       ] | ||||
|     }, | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .addMeeting { | ||||
|   :deep( .el-button--text ){ | ||||
|     .iconfont { | ||||
|       color: inherit; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -1,595 +0,0 @@ | ||||
| <template> | ||||
|   <section class="meetingDetail detail-content"> | ||||
|     <ai-detail> | ||||
|       <template #title> | ||||
|         <ai-title title="会议详情" isShowBottomBorder isShowBack @onBackClick="$parent.goBack"> | ||||
|           <template #rightBtn> | ||||
|             <p class="conference_top_area" v-if="listType==0"> | ||||
|               <!-- <el-button type="primary" v-if="detail.status==0" @click="changeMeeting('是否立即发布会议?')">立即发布</el-button> --> | ||||
|               <el-button type="primary" v-if="detail.status==1" @click="noticeMetting()">参会提醒</el-button> | ||||
|               <el-button type="primary" v-if="detail.status==0" @click="editMeeting()">修改会议</el-button> | ||||
|               <el-button class="del-btn-list" v-if="detail.status==1" @click="changeMeeting('是否取消会议?')">取消会议</el-button> | ||||
|               <el-button class="iconfont iconDelete del-btn-list" v-if="detail.status==0||detail.status==3" | ||||
|                          @click="changeMeeting('是否删除会议?')">删除会议 | ||||
|               </el-button> | ||||
|             </p> | ||||
|             <!-- v-if="detail.status==0||detail.status==3" --> | ||||
|             <p class="conference_top_area" v-if="listType==1&&info.status==1"> | ||||
|               <el-button @click="toDo" style="width:80px;" class="del-btn-list">待定</el-button> | ||||
|               <el-button @click="innerVisible=true" v-if="info.joinStatus!=2" style="width:80px;" class="del-btn-list"> | ||||
|                 请假 | ||||
|               </el-button> | ||||
|               <el-button type="primary" @click="changeMeeting('是否确认参会?')" v-if="info.joinStatus!=1" style="width:80px;"> | ||||
|                 确认参会 | ||||
|               </el-button> | ||||
|             </p> | ||||
|           </template> | ||||
|         </ai-title> | ||||
|       </template> | ||||
|       <template #content> | ||||
|         <ai-dialog | ||||
|             title="确认请假" | ||||
|             :visible.sync="innerVisible" | ||||
|             @onConfirm="queMeeting('writeInfo')" | ||||
|             @onCancel="innerVisible=false;" @close="$refs.writeInfo.resetFields()" | ||||
|             width="520px"> | ||||
|           <div class="addother_main" style="width:400px;margin:auto;"> | ||||
|             <el-form :model="writeInfo" status-icon ref="writeInfo" label-width="100px" class="demo-ruleForm"> | ||||
|               <el-form-item label="请假原因:" prop="reason" autocomplete="off" | ||||
|                             :rules="{ | ||||
|                                         required: true, message: '请假原因不能为空', trigger: 'blur' | ||||
|                                     }" | ||||
|               > | ||||
|                 <el-input v-model.trim="writeInfo.reason" autocomplete="off" size="mini" placeholder="请输入..." | ||||
|                           type="textarea" :rows="4" :maxlength="100" show-word-limit></el-input> | ||||
|               </el-form-item> | ||||
|  | ||||
|             </el-form> | ||||
|           </div> | ||||
|         </ai-dialog> | ||||
|         <ai-card title="会议说明"> | ||||
|           <template slot="content"> | ||||
|             <ai-wrapper class="mar-t16" label-width="70px" :columnsNumber="1"> | ||||
|               <ai-info-item label="会议标题:"><span>{{ info.title }}</span></ai-info-item> | ||||
|             </ai-wrapper> | ||||
|             <ai-wrapper class="mar-t16" label-width="70px" :columnsNumber="2"> | ||||
|               <ai-info-item label="发起单位:" :value="info.unitName"> | ||||
|               </ai-info-item> | ||||
|               <ai-info-item label="发起人:" :value="info.userName"> | ||||
|               </ai-info-item> | ||||
|             </ai-wrapper> | ||||
|             <ai-wrapper class="mar-t16" label-width="70px" :columnsNumber="1"> | ||||
|               <!-- <ai-info-item label="会议状态:" v-if="xq.joinStatus==1&&listType==1"><span>{{ xq.joinStatus }}</span> | ||||
|               </ai-info-item> --> | ||||
|               <ai-info-item label="发起时间:"><span>{{ info.createTime }}</span></ai-info-item> | ||||
|             </ai-wrapper> | ||||
|             <ai-wrapper class="mar-t16" label-width="70px" :columnsNumber="2"> | ||||
|               <ai-info-item label="开始时间:"><span>{{ info.startTime }}</span></ai-info-item> | ||||
|               <ai-info-item label="结束时间:"><span>{{ info.endTime }}</span></ai-info-item> | ||||
|             </ai-wrapper> | ||||
|             <ai-wrapper class="mar-t16" label-width="70px" :columnsNumber="2"> | ||||
|               <ai-info-item label="参会提醒:"><span>{{ | ||||
|                   dict.getLabel("meetingNoticeBefore", info.noticeBefore) || "-" | ||||
|                 }}</span></ai-info-item> | ||||
|               <ai-info-item label="确认提醒:"><span>{{ | ||||
|                   dict.getLabel("meetingNoticeAfter", info.noticeAfter) || "-" | ||||
|                 }}</span> | ||||
|               </ai-info-item> | ||||
|             </ai-wrapper> | ||||
|             <ai-wrapper class="mar-t16" label-width="70px" :columnsNumber="1"> | ||||
|               <ai-info-item label="会议地点:"><span>{{ info.address }}</span></ai-info-item> | ||||
|             </ai-wrapper> | ||||
|             <ai-wrapper class="mar-t16" label-width="70px" :columnsNumber="1"> | ||||
|               <ai-info-item label="会议内容:"><span v-html="info.content"></span></ai-info-item> | ||||
|             </ai-wrapper> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|         <ai-card title="会议资料"> | ||||
|           <template slot="content"> | ||||
|             <ai-file-list :fileList="info.files" :fileOps="{name: 'name', size: 'fileSizeStr'}"></ai-file-list> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|         <ai-card title="参会名单"> | ||||
|           <template slot="content"> | ||||
|             <ai-search-bar bottomBorder> | ||||
|               <template #left> | ||||
|                 <el-select v-model="search.joinStatus" placeholder="确认状态" size="small" clearable class="vc-input-160" @change="searchMeetinguser"> | ||||
|                   <el-option | ||||
|                       v-for="(item,k) in confirmStatus" | ||||
|                       :key="k" | ||||
|                       :label="item.label" | ||||
|                       :value="k"> | ||||
|                   </el-option> | ||||
|                 </el-select> | ||||
|               </template> | ||||
|               <template #right> | ||||
|                 <!-- <ai-download :instance="instance" url="/app/appepidemicbackhomerecord/export" fileName="参会名单"> | ||||
|                   <el-button icon="iconfont iconExported">导出</el-button> | ||||
|                 </ai-download> --> | ||||
|               </template> | ||||
|             </ai-search-bar> | ||||
|             <ai-table | ||||
|                 :tableData="info.attendees" | ||||
|                 :colConfigs="colConfigs" | ||||
|                 style="margin-top: 12px;" | ||||
|                 :isShowPagination="false"> | ||||
|               <el-table-column slot="meetingUserName" | ||||
|                                label="姓名" | ||||
|                                align="center" | ||||
|                                show-overflow-tooltip> | ||||
|                 <div slot-scope="{row}"> | ||||
|                   <span>{{ row.meetingUserName }}</span> | ||||
|                 </div> | ||||
|               </el-table-column> | ||||
|  | ||||
|               <el-table-column slot="meetingUnitName" | ||||
|                                label="所属部门" | ||||
|                                align="center" | ||||
|                                show-overflow-tooltip> | ||||
|                 <div slot-scope="{row}"> | ||||
|                   <span>{{ row.meetingUnitName }}</span> | ||||
|                 </div> | ||||
|               </el-table-column> | ||||
|  | ||||
|               <el-table-column slot="joinStatus" | ||||
|                                prop="joinStatus" | ||||
|                                label="确认状态" | ||||
|                                align="center" | ||||
|                                show-overflow-tooltip> | ||||
|                 <div slot-scope="{row}"> | ||||
|                   <p style="color:rgba(255,68,102,1);display:flex;justify-content:center;" v-if="row.joinStatus==2"> | ||||
|                     请假<i class="el-icon-warning" :title="row.absence" style="cursor: pointer;"></i> | ||||
|                   </p> | ||||
|                   <span v-else :style="{color:confirmStatus[row.joinStatus].color}" | ||||
|                         v-text="confirmStatus[row.joinStatus].label"/> | ||||
|                 </div> | ||||
|               </el-table-column> | ||||
|               <el-table-column slot="option" | ||||
|                                label="操作" | ||||
|                                align="center" | ||||
|                                v-if="listType==0" | ||||
|                                show-overflow-tooltip> | ||||
|                 <div slot-scope="{row}" v-if="row.joinStatus==0"> | ||||
|                   <span class="el-icon-message-solid" style="cursor: pointer;" title="参会提醒" | ||||
|                         @click="noticeMetting(row.meetingUserId)" v-if="detail.status==1"></span> | ||||
|                 </div> | ||||
|               </el-table-column> | ||||
|             </ai-table> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|       </template> | ||||
|     </ai-detail> | ||||
|   </section> | ||||
| </template> | ||||
| <script> | ||||
| import {mapState} from "vuex"; | ||||
|  | ||||
| export default { | ||||
|   name: 'meetingDetail', | ||||
|   inject: ['top'], | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function, | ||||
|     detail: Object, | ||||
|     listType: String | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       navId: '1', | ||||
|       info: {}, | ||||
|       total: 0, | ||||
|       writeInfo: {reason: ''}, | ||||
|       meetingUserList: [], | ||||
|       innerVisible: false, | ||||
|       search: {joinStatus: ''} | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.dict.load("meetingNoticeAfter", "meetingNoticeBefore").then(() => this.searchDetail(this.detail.id)) | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|     colConfigs() { | ||||
|       return [ | ||||
|         { | ||||
|           slot: 'meetingUserName' | ||||
|         }, | ||||
|         { | ||||
|           slot: 'meetingUnitName' | ||||
|         }, | ||||
|         { | ||||
|           slot: 'joinStatus', | ||||
|         }, | ||||
|         {prop: 'signInStatus', align: 'center', label: '签到', format: v => v === '1' ? '已签到' : '未签到'}, | ||||
|         { | ||||
|           slot: 'option', | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     confirmStatus() { | ||||
|       return { | ||||
|         0: {label: '未确认', color: "rgba(255,136,34,1)"}, | ||||
|         1: {label: '已确认', color: "rgba(34,102,255,1)"}, | ||||
|         2: {label: '请假', color: "rgba(255,68,102,1)"}, | ||||
|         3: {label: '待定', color: "rgba(255,136,34,1)"}, | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     toDo() { | ||||
|       this.$confirm("是否确认待定会议?").then(_ => { | ||||
|         this.instance.post("/app/appmeetinginfo/tobeConfirm", null, { | ||||
|           params: { | ||||
|             meetingId: this.detail.id, | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.$message.success("会议待定"); | ||||
|             setTimeout(_ => { | ||||
|               this.$parent.goBack(); | ||||
|             }, 800) | ||||
|           } | ||||
|         }) | ||||
|       }) | ||||
|     }, | ||||
|     searchMeetinguser() { | ||||
|       this.instance.post(`/app/appmeetinguser/list`, null, { | ||||
|         params: {...this.search, size: 999, meetingId: this.info.id} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.info.attendees = res.data.records; | ||||
|           this.total = res.data.total; | ||||
|  | ||||
|         } | ||||
|       }); | ||||
|     }, | ||||
|     searchDetail(id) { | ||||
|       this.instance.post(`/app/appmeetinginfo/info-id`, null, { | ||||
|         params: {id} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           let {files = [], content} = res.data | ||||
|           content = content.replace(/(\r\n)|(\n)/g, "<br>") | ||||
|           this.info = {...res.data, content, files}; | ||||
|           this.searchMeetinguser() | ||||
|         } | ||||
|       }); | ||||
|     }, | ||||
|     changeMeeting(title, id) { | ||||
|       this.$confirm(title, { | ||||
|         type: 'warning' | ||||
|       }).then(() => { | ||||
|         if (title == '是否立即发布会议?') { | ||||
|           this.fbMeeting(); | ||||
|         } else if (title == '是否删除会议?') { | ||||
|           this.sdMeeting(); | ||||
|         } else if (title == '是否取消会议?') { | ||||
|           this.qxMeeting(); | ||||
|         } else if (title == '是否确认参会?') { | ||||
|           this.qrMeeting(); | ||||
|         } else if (title == '是否删除此参会人员?') { | ||||
|           this.deleteMeetingPer(id) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     deleteMeetingPer(id) { | ||||
|       this.instance.post(`/app/appmeetinguser/delete`, null, | ||||
|           { | ||||
|             params: { | ||||
|               id | ||||
|             } | ||||
|           } | ||||
|       ).then(res => { | ||||
|  | ||||
|         if (res && res.code == 0) { | ||||
|           this.$message.success("删除成功!"); | ||||
|           this.searchMeetinguser(); | ||||
|         } | ||||
|  | ||||
|       }); | ||||
|     }, | ||||
|     queMeeting(formName) { | ||||
|       this.$refs[formName].validate((valid) => { | ||||
|         if (valid) { | ||||
|           this.instance.post(`/app/appmeetinginfo/absent`, null, | ||||
|               { | ||||
|                 params: { | ||||
|                   meetingId: this.detail.id, | ||||
|                   reason: this.writeInfo.reason | ||||
|                 } | ||||
|               } | ||||
|           ).then(res => { | ||||
|  | ||||
|             if (res && res.code == 0) { | ||||
|               this.innerVisible = false; | ||||
|               this.$parent.goBack(); | ||||
|             } | ||||
|  | ||||
|           }); | ||||
|         } else { | ||||
|           return false; | ||||
|         } | ||||
|       }); | ||||
|  | ||||
|     }, | ||||
|     qrMeeting() { | ||||
|       this.instance.post(`/app/appmeetinginfo/confirm`, null, | ||||
|           { | ||||
|             params: { | ||||
|               meetingId: this.detail.id | ||||
|  | ||||
|             } | ||||
|           } | ||||
|       ).then(res => { | ||||
|  | ||||
|         if (res && res.code == 0) { | ||||
|           this.$message.success("确认参会成功!") | ||||
|           this.$parent.goBack(); | ||||
|         } | ||||
|  | ||||
|       }); | ||||
|     }, | ||||
|     noticeMetting(meetingUserId) { | ||||
|       this.instance.post(`/app/appmeetinginfo/notice`, null, { | ||||
|         params: { | ||||
|           meetingId: this.detail.id, noticeALL: !meetingUserId, meetingUserId | ||||
|         } | ||||
|       }).then(res => { | ||||
|         if (res && res.code == 0) { | ||||
|           this.$message.success("提醒成功!") | ||||
|         } | ||||
|  | ||||
|       }); | ||||
|     }, | ||||
|     fbMeeting() { | ||||
|       this.instance.post(`/app/appmeetinginfo/release`, null, | ||||
|           { | ||||
|             params: { | ||||
|               meetingId: this.detail.id | ||||
|             } | ||||
|           } | ||||
|       ).then(res => { | ||||
|  | ||||
|         if (res && res.code == 0) { | ||||
|           this.$message.success("发布成功!") | ||||
|           this.$parent.goBack(); | ||||
|         } | ||||
|  | ||||
|       }); | ||||
|  | ||||
|     }, | ||||
|     sdMeeting() { | ||||
|       this.instance.post(`/app/appmeetinginfo/delete`, null, | ||||
|           { | ||||
|             params: { | ||||
|               meetingId: this.detail.id | ||||
|             } | ||||
|           } | ||||
|       ).then(res => { | ||||
|  | ||||
|         if (res && res.code == 0) { | ||||
|           this.$message.success("删除成功!"); | ||||
|           this.$parent.goBack(); | ||||
|         } | ||||
|  | ||||
|       }); | ||||
|     }, | ||||
|     qxMeeting() { | ||||
|       this.instance.post(`/app/appmeetinginfo/cancel`, null, | ||||
|           { | ||||
|             params: { | ||||
|               meetingId: this.detail.id | ||||
|             } | ||||
|           } | ||||
|       ).then(res => { | ||||
|  | ||||
|         if (res && res.code == 0) { | ||||
|           this.$message.success("取消会议成功!"); | ||||
|           this.$parent.goBack(); | ||||
|         } | ||||
|  | ||||
|       }); | ||||
|     }, | ||||
|     editMeeting() { | ||||
|       this.$parent.goBack(); | ||||
|       this.$emit('gotoEdit', this.info) | ||||
|     }, | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .meetingDetail { | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   background: rgba(255, 255, 255, 1); | ||||
|  | ||||
|   .conference_top_area { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|   } | ||||
|  | ||||
|   .user-search { | ||||
|     width: 100%; | ||||
|     height: 48px; | ||||
|     display: flex; | ||||
|     justify-content: space-between; | ||||
|     align-items: center; | ||||
|  | ||||
|     .float-right { | ||||
|       width: calc(100% - 200px); | ||||
|       text-align: right; | ||||
|     } | ||||
|  | ||||
|     .input-162 { | ||||
|       display: inline-block; | ||||
|       width: 162px; | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   .content { | ||||
|     width: 1000px; | ||||
|     height: calc(100% - 50px); | ||||
|     overflow: hidden; | ||||
|     margin: auto; | ||||
|     display: flex; | ||||
|     justify-content: space-around; | ||||
|  | ||||
|     .content-left { | ||||
|       width: 160px; | ||||
|       height: 100%; | ||||
|  | ||||
|       .content-left-nav { | ||||
|         width: 158px; | ||||
|         background-color: #ffffff; | ||||
|         border-radius: 4px; | ||||
|         border: solid 1px #eeeeee; | ||||
|         margin-top: 56px; | ||||
|         overflow: hidden; | ||||
|  | ||||
|         li { | ||||
|           height: 48px; | ||||
|           line-height: 48px; | ||||
|           padding-left: 24px; | ||||
|           font-size: 14px; | ||||
|           font-weight: normal; | ||||
|           font-stretch: normal; | ||||
|           letter-spacing: 0; | ||||
|           color: #666666; | ||||
|           cursor: pointer; | ||||
|           border-left: 3px solid transparent; | ||||
|  | ||||
|           &:hover { | ||||
|             border-left: 3px solid #5088ff; | ||||
|           } | ||||
|  | ||||
|           a { | ||||
|             display: block; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         .navActive { | ||||
|           border-left: 3px solid #5088ff; | ||||
|           color: #5088ff; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     .content-right { | ||||
|       width: 780px; | ||||
|       height: calc(100% - 80px); | ||||
|       overflow-y: auto; | ||||
|       margin-left: 40px; | ||||
|       box-sizing: border-box; | ||||
|       overflow-x: hidden; | ||||
|  | ||||
|       .content-right-title { | ||||
|         width: 780px; | ||||
|         height: 56px; | ||||
|         margin-bottom: 16px; | ||||
|         box-shadow: inset 0px -1px 0px 0px #dad5d5; | ||||
|         display: flex; | ||||
|         justify-content: space-between; | ||||
|         align-items: center; | ||||
|  | ||||
|         span { | ||||
|           display: block; | ||||
|           width: 150px; | ||||
|           height: 56px; | ||||
|           line-height: 56px; | ||||
|           color: #333333; | ||||
|           font-weight: bold; | ||||
|  | ||||
|           &:nth-of-type(2) { | ||||
|             text-align: right; | ||||
|             width: 200px; | ||||
|           } | ||||
|  | ||||
|         } | ||||
|  | ||||
|  | ||||
|       } | ||||
|  | ||||
|       .flie { | ||||
|         width: 100%; | ||||
|         height: 40px; | ||||
|         line-height: 40px; | ||||
|         padding: 0 8px; | ||||
|         box-sizing: border-box; | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: space-between; | ||||
|         font-size: 14px; | ||||
|         color: rgba(51, 51, 51, 1); | ||||
|         background: rgba(255, 255, 255, 1); | ||||
|         border-radius: 4px; | ||||
|         border: 1px solid rgba(208, 212, 220, 1); | ||||
|         margin-bottom: 16px; | ||||
|         cursor: pointer; | ||||
|  | ||||
|         p { | ||||
|           display: flex; | ||||
|           justify-content: flex-start; | ||||
|           align-items: center | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .meeting_name { | ||||
|         width: 100%; | ||||
|         padding-bottom: 25px; | ||||
|         font-size: 16px; | ||||
|         box-shadow: inset 0px -1px 0px 0px #dad5d5; | ||||
|         position: relative; | ||||
|         overflow: hidden; | ||||
|  | ||||
|         .title { | ||||
|           color: #333333; | ||||
|           height: 28px; | ||||
|           line-height: 28px; | ||||
|           margin-left: 0; | ||||
|           font-weight: bold; | ||||
|           margin-top: 14px; | ||||
|         } | ||||
|  | ||||
|         ul { | ||||
|           overflow: hidden; | ||||
|  | ||||
|           li { | ||||
|             width: 33.3%; | ||||
|             float: left; | ||||
|             line-height: 28px; | ||||
|             font-size: 14px; | ||||
|  | ||||
|             span { | ||||
|               width: 70px; | ||||
|               display: block; | ||||
|               float: left; | ||||
|               color: rgba(153, 153, 153, 1) | ||||
|             } | ||||
|  | ||||
|             p { | ||||
|               width: calc(100% - 70px); | ||||
|               float: left; | ||||
|               color: rgba(51, 51, 51, 1); | ||||
|             } | ||||
|           ; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         .svg { | ||||
|           width: 88px; | ||||
|           height: 88px; | ||||
|           position: absolute; | ||||
|           right: -20px; | ||||
|           bottom: -20px; | ||||
|         } | ||||
|  | ||||
|       } | ||||
|  | ||||
|  | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -1,259 +0,0 @@ | ||||
| <template> | ||||
|   <ai-list isTabs> | ||||
|     <template #content> | ||||
|       <ai-search-bar> | ||||
|         <template #left> | ||||
|           <el-button type="primary" @click="addMetting()">发起会议</el-button> | ||||
|           <ai-select | ||||
|               v-model="search.meetingStatus" | ||||
|               @change="getTableData()" | ||||
|               placeholder="会议状态" | ||||
|               :selectList="dict.getDict($parent.name==0 ? 'meetingStatusSelect' : 'meetingStatus')" | ||||
|           ></ai-select> | ||||
|           <ai-select | ||||
|               v-if="$parent.name==0" | ||||
|               v-model="search.confirmStatus" | ||||
|               @change="getTableData()" | ||||
|               placeholder="确认状态" | ||||
|               :selectList="dict.getDict('confirmStatus')" | ||||
|           ></ai-select> | ||||
|         </template> | ||||
|         <template #right> | ||||
|           <el-input | ||||
|             v-model="search.param" | ||||
|             v-throttle="() => {search.current=1,getTableData()}" | ||||
|             @keyup.enter.native="search.current=1,getTableData()" | ||||
|             placeholder="会议标题/地点" | ||||
|             size="small" suffix-icon="iconfont iconSearch" clearable @clear="getTableData()"></el-input> | ||||
|         </template> | ||||
|       </ai-search-bar> | ||||
|       <div class="list_main"> | ||||
|         <div class="no-data" style="height:160px;" v-if="tableData.length==0"></div> | ||||
|         <ul v-if="tableData.length>0"> | ||||
|           <li v-for="(item,index) in tableData" :key="index" @click="goDetail(item)"> | ||||
|             <p> | ||||
|               <span class="conference_title">{{ item.title }}</span> | ||||
|               <span class="time" v-if="item.status==1">{{ item.expirationTime }}</span> | ||||
|             </p> | ||||
|             <p style="width:80%;"> | ||||
|               <el-row type="flex" align="middle" class="unit">发起人:{{ item.userName }} | ||||
|               </el-row> | ||||
|               <el-row type="flex" align="middle" class="unit">发起单位:{{ item.unitName }} | ||||
|               </el-row> | ||||
|             </p> | ||||
|             <p style="width:80%;"> | ||||
|               <span class="unit">会议时间:{{ item.startTime.substring(0, 16) + '至' + item.endTime.substring(0, 16) }}</span> | ||||
|               <el-tooltip :content="item.address" placement="top-start" effect="light"> | ||||
|                 <span class="unit" v-if="item.address.length>12">会议地点:{{ | ||||
|                     item.address.substring(0, 12) + '....' | ||||
|                   }}</span> | ||||
|                 <span class="unit" v-else>会议地点:{{ item.address }}</span> | ||||
|               </el-tooltip> | ||||
|             </p> | ||||
|             <!-- <p style="width:80%;"> | ||||
|                   <span | ||||
|                     class="unit">会议时间:{{ | ||||
|                       item.startTime.substring(0, 16) + '至' + item.endTime.substring(0, 16) | ||||
|                     }}</span> | ||||
|               <el-tooltip :content="item.address" placement="top-start" effect="light"> | ||||
|                     <span class="unit address" | ||||
|                           v-if="item.address.length>12">会议地点:{{ item.address.substring(0, 12) + '....' }}</span> | ||||
|                 <span class="unit address" v-else>会议地点:{{ item.address }}</span> | ||||
|               </el-tooltip> | ||||
|             </p> --> | ||||
|             <h5 :class="{color0:item.status==0,color1:item.status==1,color2:item.status==2,color3:item.status==3,color4:item.status==4}"> | ||||
|               <span v-if="item.status==0">草稿箱</span> | ||||
|               <span v-if="item.status==1">未开始</span> | ||||
|               <span v-if="item.status==2">进行中</span> | ||||
|               <span v-if="item.status==3">已取消</span> | ||||
|               <span v-if="item.status==4">已结束</span> | ||||
|             </h5> | ||||
|             <ai-icon class="svg" v-if="item.joinStatus==0" type="svg" icon="iconunidentified"/> | ||||
|             <ai-icon class="svg" v-else-if="item.joinStatus==1" type="svg" icon="iconidentified"/> | ||||
|             <ai-icon class="svg" v-else-if="item.joinStatus==2" type="svg" icon="iconyiqingjia"/> | ||||
|             <ai-icon class="svg" v-else-if="item.joinStatus==3" type="svg" icon="icondaiding"/> | ||||
|           </li> | ||||
|         </ul> | ||||
|       </div> | ||||
|       <div class="pagination" v-if="tableData.length>0"> | ||||
|         <el-pagination | ||||
|             @current-change="handleCurrentChange" | ||||
|             @size-change="handleSizeChange" | ||||
|             background | ||||
|             :current-page.sync="search.current" | ||||
|             :page-sizes="[5, 10, 50, 100,200]" | ||||
|             :page-size="search.size" | ||||
|             layout="total,prev, pager, next,sizes,jumper" | ||||
|             :total="total"> | ||||
|         </el-pagination> | ||||
|       </div> | ||||
|     </template> | ||||
|   </ai-list> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "list", | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function, | ||||
|     listType: String | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       search: { | ||||
|         meetingStatus: '', | ||||
|         confirmStatus: '', | ||||
|         param: '', | ||||
|         current: 1, | ||||
|         size: 10, | ||||
|         listType: '' | ||||
|       }, | ||||
|       tableData: [], | ||||
|       total: 0, | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     goDetail(item) { | ||||
|       this.$emit('goPage', { | ||||
|         row: item, | ||||
|         comp: 'detail' | ||||
|       }); | ||||
|     }, | ||||
|     addMetting() { | ||||
|       this.$emit('goPage', { | ||||
|         row: {}, | ||||
|         comp: 'addMeeting' | ||||
|       }); | ||||
|     }, | ||||
|  | ||||
|     getTableData() { | ||||
|       this.instance.post(`/app/appmeetinginfo/list`, null, { | ||||
|         params: { | ||||
|           ...this.search, | ||||
|           listType: this.listType | ||||
|         } | ||||
|       }).then(res => { | ||||
|         if (res && res.data) { | ||||
|           this.tableData = res.data.records; | ||||
|           this.total = res.data.total; | ||||
|  | ||||
|         } | ||||
|       }); | ||||
|     }, | ||||
|     handleCurrentChange(val) { | ||||
|       this.search.current = val; | ||||
|       this.getTableData(); | ||||
|     }, | ||||
|     handleSizeChange(val) { | ||||
|       this.search.size = val; | ||||
|       this.getTableData(); | ||||
|     }, | ||||
|   }, | ||||
|   created() { | ||||
|     this.dict.load("confirmStatus", "meetingStatus", "meetingStatusSelect").then(_ => this.getTableData()) | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .list_main { | ||||
|   width: 100%; | ||||
|  | ||||
|   ul { | ||||
|     overflow: hidden; | ||||
|     padding: 0; | ||||
|     margin: 0; | ||||
|  | ||||
|     li { | ||||
|       width: 100%; | ||||
|       height: 107px; | ||||
|       background: rgba(255, 255, 255, 1); | ||||
|       border-radius: 4px; | ||||
|       border: 1px solid rgba(216, 224, 232, 1); | ||||
|       box-sizing: border-box; | ||||
|       padding: 16px 16px 16px 50px; | ||||
|       margin-top: 8px; | ||||
|       cursor: pointer; | ||||
|       position: relative; | ||||
|       overflow: hidden; | ||||
|  | ||||
|       p { | ||||
|         width: 100%; | ||||
|         height: 25px; | ||||
|         display: flex; | ||||
|         justify-content: space-between; | ||||
|         align-items: center; | ||||
|  | ||||
|         .conference_title { | ||||
|           color: rgba(51, 51, 51, 1); | ||||
|           font-size: 16px; | ||||
|           font-weight: bold; | ||||
|         } | ||||
|  | ||||
|         .time { | ||||
|           font-size: 14px; | ||||
|           color: #2266FF; | ||||
|         } | ||||
|  | ||||
|         .unit { | ||||
|           font-size: 14px; | ||||
|           width: 50%; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       h5 { | ||||
|         width: 100px; | ||||
|         height: 20px; | ||||
|         line-height: 20px; | ||||
|         text-align: center; | ||||
|         position: absolute; | ||||
|         left: -22px; | ||||
|         top: 10px; | ||||
|         box-sizing: border-box; | ||||
|         padding-right: 8px; | ||||
|         font-size: 12px; | ||||
|         transform: rotate(-45deg); | ||||
|         background: #FFF3E8; | ||||
|         color: rgba(255, 136, 34, 1); | ||||
|         box-shadow: -1px 1px 0px 0px rgba(216, 224, 232, 1), 1px -1px 0px 0px rgba(216, 224, 232, 1); | ||||
|         margin: 0; | ||||
|       } | ||||
|  | ||||
|       .color0 { | ||||
|         color: #2244FF; | ||||
|         background: #EFF6FF; | ||||
|       } | ||||
|  | ||||
|       .color1 { | ||||
|         background: #FFF3E8; | ||||
|         color: rgba(255, 136, 34, 1); | ||||
|       } | ||||
|  | ||||
|       .color2 { | ||||
|         background: #EFF6FF; | ||||
|         color: #2266FF; | ||||
|       } | ||||
|  | ||||
|       .color3 { | ||||
|         background-color: #D8E0E8; | ||||
|         color: #999999; | ||||
|       } | ||||
|  | ||||
|       .color4 { | ||||
|         color: #2EA222; | ||||
|         background-color: #D8E0E8; | ||||
|       } | ||||
|  | ||||
|       .svg { | ||||
|         width: 88px; | ||||
|         height: 88px; | ||||
|         position: absolute; | ||||
|         right: -20px; | ||||
|         bottom: -20px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -1,186 +0,0 @@ | ||||
| <template> | ||||
|   <section class="AppCorporateSeal"> | ||||
|       <ai-list v-if="showList"> | ||||
|         <template slot="title"> | ||||
|           <ai-title title="企业印章" :isShowBottomBorder="true"></ai-title> | ||||
|         </template> | ||||
|         <template slot="content"> | ||||
|           <div class="signaturePane"> | ||||
|             <div class="signatureCard" v-for="(op,i) in signatures" :key="i"> | ||||
|               <div class="default" v-if="op.isDefault==1">默认</div> | ||||
|               <div class="body"> | ||||
|                 <el-image :src="`data:image/png;base64,${op.signSealData}`"/> | ||||
|               </div> | ||||
|               <div class="footer"> | ||||
|                 <el-button type="text" :disabled="op.isDefault==1" @click.stop="handleSetDefault(op.id)">设为默认</el-button> | ||||
|                 <hr/> | ||||
|                 <el-button type="text" :disabled="op.isDefault==1||op.signType==1" @click.stop="handleDelete(op.id)">删除 | ||||
|                 </el-button> | ||||
|               </div> | ||||
|             </div> | ||||
|             <div class="signatureCard add" @click="showList=false"> | ||||
|               <ai-icon icon="iconAdd" size="32px"/> | ||||
|               <span>点击添加印章</span> | ||||
|             </div> | ||||
|           </div> | ||||
|         </template> | ||||
|       </ai-list> | ||||
|     <seal-detail v-else/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import SealDetail from "./sealDetail"; | ||||
|  | ||||
|   export default { | ||||
|     name: "AppCorporateSeal", | ||||
|     label: "企业印章", | ||||
|     components: {SealDetail}, | ||||
|     provide() { | ||||
|       return { | ||||
|         seal: this | ||||
|       } | ||||
|     }, | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object, | ||||
|       permissions: Function | ||||
|     }, | ||||
|     data() { | ||||
|       return { | ||||
|         signatures: [], | ||||
|         showList: true | ||||
|       } | ||||
|     }, | ||||
|     created() { | ||||
|       this.getSignatures() | ||||
|     }, | ||||
|     methods: { | ||||
|       getSignatures() { | ||||
|         this.instance.post("/app/syssignaccount/list", null, { | ||||
|           params: { | ||||
|             signType: 2, | ||||
|             size: 999 | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res?.data) { | ||||
|             this.signatures = res.data.records | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|       handleSetDefault(id) { | ||||
|         this.$confirm("是否设置该印章为默认印章?").then(() => { | ||||
|           this.instance.post("/app/syssignaccount/default", null, {params: {id, listType: 1}}).then(res => { | ||||
|             if (res?.code == 0) { | ||||
|               this.$message.success("设置成功!") | ||||
|               this.getSignatures() | ||||
|             } | ||||
|           }).catch(() => 0) | ||||
|         }) | ||||
|       }, | ||||
|       handleDelete(ids) { | ||||
|         this.$confirm("是否删除该印章?").then(() => { | ||||
|           this.instance.post("/app/syssignaccount/delete", null, {params: {ids}}).then(res => { | ||||
|             if (res?.code == 0) { | ||||
|               this.$message.success("删除成功!") | ||||
|               this.getSignatures() | ||||
|             } | ||||
|           }).catch(() => 0) | ||||
|         }) | ||||
|       }, | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .AppCorporateSeal { | ||||
|     height: 100%; | ||||
|     background: #f3f6f9; | ||||
|  | ||||
|     :deep( .signaturePane ){ | ||||
|       display: flex; | ||||
|       gap: 16px; | ||||
|       flex-wrap: wrap; | ||||
|       padding: 16px; | ||||
|  | ||||
|       .signatureCard { | ||||
|         width: 290px; | ||||
|         height: 258px; | ||||
|         background: #FFFFFF; | ||||
|         border-radius: 4px; | ||||
|         position: relative; | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         overflow: hidden; | ||||
|  | ||||
|         &.add { | ||||
|           justify-content: center; | ||||
|           align-items: center; | ||||
|           color: #666666; | ||||
|           cursor: pointer; | ||||
|  | ||||
|           .AiIcon { | ||||
|             width: 32px; | ||||
|             height: 32px; | ||||
|             font-size: 32px; | ||||
|           } | ||||
|  | ||||
|           & > span { | ||||
|             font-size: 12px; | ||||
|             line-height: 16px; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         .default { | ||||
|           position: absolute; | ||||
|           width: 56px; | ||||
|           height: 24px; | ||||
|           background: #3573FF; | ||||
|           border-radius: 0 0 4px 0; | ||||
|           top: 0; | ||||
|           left: 0; | ||||
|           text-align: center; | ||||
|           line-height: 24px; | ||||
|           font-size: 12px; | ||||
|           color: #FFF; | ||||
|         } | ||||
|  | ||||
|         .body { | ||||
|           min-height: 0; | ||||
|           flex: 1; | ||||
|           display: flex; | ||||
|           justify-content: center; | ||||
|           align-items: center; | ||||
|           padding: 50px; | ||||
|           cursor: pointer; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         .footer { | ||||
|           flex-shrink: 0; | ||||
|           height: 40px; | ||||
|           background: rgba(#30426F, .5); | ||||
|           display: flex; | ||||
|           align-items: center; | ||||
|           padding: 8px 0; | ||||
|           box-sizing: border-box; | ||||
|  | ||||
|           hr { | ||||
|             height: 100%; | ||||
|             border-color: rgba(#fff, .5); | ||||
|           } | ||||
|  | ||||
|           & > .el-button { | ||||
|             flex: 1; | ||||
|             color: #fff; | ||||
|  | ||||
|             &[disabled] { | ||||
|               color: rgba(#fff, .5); | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
| </style> | ||||
| @@ -1,235 +0,0 @@ | ||||
| <template> | ||||
|   <section class="sealDetail"> | ||||
|     <ai-detail> | ||||
|       <template #title> | ||||
|         <ai-title title="添加企业印章" isShowBack isShowBottomBorder @onBackClick="back"/> | ||||
|       </template> | ||||
|       <template #content> | ||||
|         <el-form size="small" ref="SealForm" :model="form" :rules="rules" label-suffix=":" label-width="160px"> | ||||
|           <ai-title title="单位信息" isShowBottomBorder/> | ||||
|           <el-form-item label="单位名称" prop="organizeName"> | ||||
|             <el-input clearable v-model="form.organizeName" placeholder="请输入..." maxlength="25" show-word-limit/> | ||||
|           </el-form-item> | ||||
|           <el-form-item label="单位类型" prop="organizeType"> | ||||
|             <el-select clearable v-model="form.organizeType" placeholder="请输入..."> | ||||
|               <el-option v-for="(op,i) in $dict.getDict('organizeType')" :key="i" :value="op.dictValue" | ||||
|                          :label="op.dictName"/> | ||||
|             </el-select> | ||||
|           </el-form-item> | ||||
|           <div class="flexFillRow"> | ||||
|             <el-form-item label="企业注册类型" prop="organRegType"> | ||||
|               <el-select clearable v-model="form.organRegType" placeholder="请输入..."> | ||||
|                 <el-option v-for="(op,i) in $dict.getDict('organRegType')" :key="i" :value="op.dictValue" | ||||
|                            :label="op.dictName"/> | ||||
|               </el-select> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="!!form.organRegType" :label="$dict.getLabel('organRegType',form.organRegType)" | ||||
|                           prop="organCode"> | ||||
|               <el-input clearable v-model="form.organCode" placeholder="请输入..."/> | ||||
|             </el-form-item> | ||||
|             <div v-else/> | ||||
|           </div> | ||||
|           <div class="flexFillRow"> | ||||
|             <el-form-item label="注册类型" prop="registerType"> | ||||
|               <el-select clearable v-model="form.registerType" placeholder="请输入..."> | ||||
|                 <el-option v-for="(op,i) in $dict.getDict('registerType')" :key="i" :value="op.dictValue" | ||||
|                            :label="op.dictName"/> | ||||
|               </el-select> | ||||
|             </el-form-item> | ||||
|             <div/> | ||||
|           </div> | ||||
|           <template v-if="form.registerType==1"> | ||||
|             <div class="flexFillRow"> | ||||
|               <el-form-item label="代理人姓名" prop="agentName"> | ||||
|                 <el-input clearable v-model="form.agentName" placeholder="请输入..."/> | ||||
|               </el-form-item> | ||||
|               <el-form-item label="代理人身份证号" prop="agentIdNumber"> | ||||
|                 <el-input clearable v-model="form.agentIdNumber" placeholder="请输入..."/> | ||||
|               </el-form-item> | ||||
|             </div> | ||||
|             <div class="flexFillRow"> | ||||
|               <el-form-item label="代理人手机号" prop="signPhone"> | ||||
|                 <el-input clearable v-model="form.signPhone" placeholder="请输入..."/> | ||||
|               </el-form-item> | ||||
|               <div/> | ||||
|             </div> | ||||
|           </template> | ||||
|           <template v-if="form.registerType==2"> | ||||
|             <div class="flexFillRow"> | ||||
|               <el-form-item label="法人姓名" prop="legalName"> | ||||
|                 <el-input clearable v-model="form.legalName" placeholder="请输入..."/> | ||||
|               </el-form-item> | ||||
|               <el-form-item label="法人身份证号" prop="legalIdNumber"> | ||||
|                 <el-input clearable v-model="form.legalIdNumber" placeholder="请输入..."/> | ||||
|               </el-form-item> | ||||
|             </div> | ||||
|             <div class="flexFillRow"> | ||||
|               <el-form-item label="法人手机号" prop="signPhone"> | ||||
|                 <el-input clearable v-model="form.signPhone" placeholder="请输入..."/> | ||||
|               </el-form-item> | ||||
|               <div/> | ||||
|             </div> | ||||
|           </template> | ||||
|           <ai-title title="印章信息" isShowBottomBorder/> | ||||
|           <el-form-item class="sealImageTypes" label="生成印章类型" prop="organizeTemplateType"> | ||||
|             <div v-for="(op,i) in sealImageTypes" :key="i" class="item" @click="form.organizeTemplateType=op.value"> | ||||
|               <el-image :src="op.image" fit="contain"/> | ||||
|               <el-radio :label="op.value" v-model="form.organizeTemplateType">{{ op.name }}</el-radio> | ||||
|             </div> | ||||
|           </el-form-item> | ||||
|           <el-form-item label="横向文内容" prop="htext"> | ||||
|             <el-input clearable v-model="form.htext" placeholder="0-8个字,如合同专用章,财务专用章等" maxlength="8" show-word-limit/> | ||||
|           </el-form-item> | ||||
|           <el-form-item label="下弦文内容" prop="qtext"> | ||||
|             <el-input clearable v-model="form.qtext" placeholder="0-20个字,下弦文是指的贵司公章底部一串防伪数字" maxlength="20" | ||||
|                       show-word-limit/> | ||||
|           </el-form-item> | ||||
|         </el-form> | ||||
|       </template> | ||||
|       <template #footer> | ||||
|         <el-button @click="back">取消</el-button> | ||||
|         <el-button type="primary" @click="handleSubmit" v-loading="loading">提交</el-button> | ||||
|       </template> | ||||
|     </ai-detail> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {ID} from "dui/lib/js/utils"; | ||||
|  | ||||
| export default { | ||||
|   name: "sealDetail", | ||||
|   inject: ['seal'], | ||||
|   data() { | ||||
|     return { | ||||
|       form: { | ||||
|         organizeTemplateType: "STAR", | ||||
|         organizeType: "4", | ||||
|         organRegType: "NORMAL", | ||||
|         registerType: "1" | ||||
|       }, | ||||
|       loading: false | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     sealImageTypes() { | ||||
|       return this.$dict.getDict("organizeTemplateType")?.map(e => ({ | ||||
|         image: e.dictColor, | ||||
|         value: e.dictValue, | ||||
|         name: e.dictName | ||||
|       })) || [] | ||||
|     }, | ||||
|     rules() { | ||||
|       return { | ||||
|         organizeName: [{required: true, message: "请填写单位名称"}], | ||||
|         organCode: [{required: true, message: `请填写${this.$dict.getLabel('organRegType', this.form.organRegType)}`}], | ||||
|         legalName: [{required: true, message: "请填写法人姓名"}], | ||||
|         agentName: [{required: true, message: "请填写代理人姓名"}], | ||||
|         organizeTemplateType: [{required: true}], | ||||
|         htext: [{required: true, message: "请填写横向文内容"}], | ||||
|         qtext: [{required: true, message: "请填写下弦文内容"}], | ||||
|         organizeType: [{required: true, message: "请选择单位类型"}], | ||||
|         organRegType: [{required: true, message: "请选择企业注册类型"}], | ||||
|         registerType: [{required: true, message: "请选择注册类型"}], | ||||
|         signPhone: [ | ||||
|           {required: true, message: "请填写手机号码"}, | ||||
|           {pattern: /^1[3456789]\d{9}$/, message: "手机号码格式有误"} | ||||
|         ], | ||||
|         legalIdNumber: [ | ||||
|           {required: true, message: "请填写法人身份证号码"}, | ||||
|           {validator: (r, v, cb) => cb(ID.check(v) ? undefined : "身份证号码格式有误")} | ||||
|         ], | ||||
|         agentIdNumber: [ | ||||
|           {required: true, message: "请填写代理人身份证号码"}, | ||||
|           {validator: (r, v, cb) => cb(ID.check(v) ? undefined : "身份证号码格式有误")} | ||||
|         ], | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.$dict.load("registerType", "organRegType", "organizeType", "organizeTemplateType") | ||||
|   }, | ||||
|   methods: { | ||||
|     back() { | ||||
|       this.seal.showList = true | ||||
|       this.seal.getSignatures() | ||||
|     }, | ||||
|     handleSubmit() { | ||||
|       this.$refs.SealForm.validate(v => { | ||||
|         if (v) { | ||||
|           this.loading = true | ||||
|           this.seal.instance.post("/app/syssignaccount/register", { | ||||
|             userType: 0, | ||||
|             signType: 2, | ||||
|             signPhone: this.form.signPhone, | ||||
|             registerInfo: {...this.form, legalArea: 0}, | ||||
|             style: {...this.form, sealColor: "RED"} | ||||
|           }).then(res => { | ||||
|             this.loading = false | ||||
|             if (res?.code == 0) { | ||||
|               this.$message.success("添加成功!") | ||||
|               this.back() | ||||
|             } | ||||
|           }).catch(() => this.loading = false) | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .sealDetail { | ||||
|   height: inherit; | ||||
|  | ||||
|   :deep( .ai-detail__content--wrapper ){ | ||||
|     .el-form { | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       gap: 24px; | ||||
|     } | ||||
|  | ||||
|     .el-form-item { | ||||
|       margin-bottom: 0; | ||||
|  | ||||
|       .el-select { | ||||
|         width: 100%; | ||||
|       } | ||||
|  | ||||
|       &.sealImageTypes > .el-form-item__content { | ||||
|         display: flex; | ||||
|         gap: 40px; | ||||
|  | ||||
|         .item { | ||||
|           display: flex; | ||||
|           flex-direction: column; | ||||
|           align-items: center; | ||||
|           justify-content: center; | ||||
|           gap: 8px; | ||||
|           cursor: pointer; | ||||
|  | ||||
|           .el-image { | ||||
|             width: 80px; | ||||
|             height: 80px; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .flexFillRow { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|  | ||||
|       & > * { | ||||
|         flex: 1; | ||||
|         min-width: 0; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   :deep( .ai-detail__footer > .el-button ){ | ||||
|     width: 92px; | ||||
|     height: 32px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -1,365 +0,0 @@ | ||||
| <template> | ||||
|   <section class="personal-signature"> | ||||
|     <ai-list v-if="!showPhonePage"> | ||||
|       <ai-title slot="title" title="个人签名" :isShowBottomBorder="false"/> | ||||
|       <template #custom> | ||||
|         <div class="signaturePane"> | ||||
|           <div class="signatureCard" v-for="(op,i) in signatures" :key="i"> | ||||
|             <div class="default" v-if="op.isDefault==1">默认</div> | ||||
|             <div class="body"> | ||||
|               <el-image :src="`data:image/png;base64,${op.signSealData}`"/> | ||||
|             </div> | ||||
|             <div class="footer"> | ||||
|               <el-button type="text" :disabled="op.isDefault==1" @click.stop="handleSetDefault(op.id)">设为默认</el-button> | ||||
|               <hr/> | ||||
|               <el-button type="text" :disabled="op.isDefault==1||op.signType>0" @click.stop="handleDelete(op.id)">删除 | ||||
|               </el-button> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div class="signatureCard add" @click="dialog=true"> | ||||
|             <ai-icon icon="iconAdd" size="32px"/> | ||||
|             <span>点击添加签名</span> | ||||
|           </div> | ||||
|         </div> | ||||
|       </template> | ||||
|     </ai-list> | ||||
|     <ai-dialog :visible.sync="dialog" v-bind="dialogConf" @onConfirm="handleSubmit" | ||||
|                @closed="form={},drawPlaceholder=true,qrCode=null,showQRCode=false,getSignatures()"> | ||||
|       <ai-drawer v-if="hasAuthed" :seal.sync="sealData" ref="aiDrawer"> | ||||
|         <template #tools> | ||||
|           <el-popover trigger="manual" v-model="showQRCode"> | ||||
|             <el-image :src="qrCode"/> | ||||
|             <div class="writeInPhone" slot="reference" @click.stop="showQR"> | ||||
|               <ai-icon icon="iconEwm"/> | ||||
|               <span>手机签名</span> | ||||
|             </div> | ||||
|           </el-popover> | ||||
|         </template> | ||||
|       </ai-drawer> | ||||
|       <el-form size="small" :model="form" ref="authForm" :rules="rules" class="authZone" v-else label-suffix=":" | ||||
|                label-width="100px"> | ||||
|         <el-alert type="warning" title="第一次添加个人签名,需先进行实名认证" show-icon :closable="false"/> | ||||
|         <el-form-item label="姓名" prop="personName"> | ||||
|           <el-input v-model="form.personName" clearable placeholder="姓名"/> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="身份证号" prop="idNumber"> | ||||
|           <el-input v-model="form.idNumber" clearable placeholder="身份证号"/> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="手机号码" prop="signPhone"> | ||||
|           <el-input v-model="form.signPhone" clearable placeholder="手机号码"/> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|     </ai-dialog> | ||||
|     <draw-in-phone v-if="showPhonePage"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapState} from "vuex"; | ||||
| import DrawInPhone from "./drawInPhone"; | ||||
|  | ||||
| export default { | ||||
|   name: "AppPersonalSignature", | ||||
|   label: "个人签名", | ||||
|   components: {DrawInPhone}, | ||||
|   provide() { | ||||
|     return { | ||||
|       signature: this | ||||
|     } | ||||
|   }, | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|     hasAuthed() { | ||||
|       return this.signatures.length > 0 | ||||
|     }, | ||||
|     dialogConf() { | ||||
|       return this.hasAuthed ? { | ||||
|         title: "手写签名", | ||||
|         width: '720px' | ||||
|       } : { | ||||
|         title: "实名认证", | ||||
|         width: '520px' | ||||
|       } | ||||
|     }, | ||||
|     rules() { | ||||
|       return { | ||||
|         personName: [{required: true, message: "请填写姓名"}], | ||||
|         signPhone: [ | ||||
|           {required: true, message: "请填写手机号码"}, | ||||
|           {pattern: /^1[3456789]\d{9}$/, message: "手机号码格式有误"} | ||||
|         ], | ||||
|         idNumber: [ | ||||
|           {required: true, message: "请填写身份证号码"}, | ||||
|           {validator: (r, v, cb) => cb(ID.check(v) ? undefined : "身份证号码格式有误")} | ||||
|         ], | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       signatures: [], | ||||
|       dialog: false, | ||||
|       form: {}, | ||||
|       sealData: null, | ||||
|       qrCode: null, | ||||
|       showQRCode: false, | ||||
|       showPhonePage: false, | ||||
|       loading: false, | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     if (this.$route.query.userId && this.$route.hash == "#phone") { | ||||
|       this.showPhonePage = true | ||||
|     } else { | ||||
|       this.getSignatures() | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getSignatures() { | ||||
|       this.instance.post("/app/syssignaccount/list", null, { | ||||
|         params: { | ||||
|           size: 999 | ||||
|         } | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.signatures = res.data.records | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleSubmit() { | ||||
|       if (this.loading) return | ||||
|       if (this.hasAuthed && this.$refs['aiDrawer'].drawPlaceholder) return this.$message.error("请签名") | ||||
|       this.loading = true | ||||
|       if (this.hasAuthed) { | ||||
|         let sealData = this.sealData?.replace(/data:image\/png;base64,/, '') | ||||
|         sealData && this.instance({ | ||||
|           url: '/app/syssignaccount/upload-sealdata', | ||||
|           headers: {"Content-Type": "application/json"}, | ||||
|           method: 'post', | ||||
|           params: {userId: this.user.info.id}, | ||||
|           data: sealData | ||||
|         }).then(res => { | ||||
|           this.loading = false | ||||
|           if (res?.code == 0) { | ||||
|             this.dialog = false | ||||
|             this.$message.success("添加成功!") | ||||
|             this.getSignatures() | ||||
|           } | ||||
|         }).catch(() => { | ||||
|           this.loading = false | ||||
|         }) | ||||
|       } else { | ||||
|         this.$refs.authForm.validate(v => { | ||||
|           if (v) { | ||||
|             this.instance.post("/app/syssignaccount/register", { | ||||
|               signPhone: this.form.signPhone, | ||||
|               signType: 1, | ||||
|               userType: 0, | ||||
|               registerInfo: {...this.form}, | ||||
|               style: {personTemplateType: 'RECTANGLE', sealColor: 'RED'} | ||||
|             }).then(res => { | ||||
|               this.loading = false | ||||
|               if (res?.code == 0) { | ||||
|                 this.dialog = false | ||||
|                 this.$message.success("认证成功!") | ||||
|                 this.getSignatures() | ||||
|               } | ||||
|             }).catch(() => { | ||||
|               this.loading = false | ||||
|             }) | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     }, | ||||
|     handleSetDefault(id) { | ||||
|       this.$confirm("是否设置该签名为默认签名?").then(() => { | ||||
|         this.instance.post("/app/syssignaccount/default", null, {params: {id, listType: 0}}).then(res => { | ||||
|           if (res?.code == 0) { | ||||
|             this.$message.success("设置成功!") | ||||
|             this.getSignatures() | ||||
|           } | ||||
|         }).catch(() => 0) | ||||
|       }) | ||||
|     }, | ||||
|     handleDelete(ids) { | ||||
|       this.$confirm("是否删除该签名?").then(() => { | ||||
|         this.instance.post("/app/syssignaccount/delete", null, {params: {ids}}).then(res => { | ||||
|           if (res?.code == 0) { | ||||
|             this.$message.success("删除成功!") | ||||
|             this.getSignatures() | ||||
|           } | ||||
|         }).catch(() => 0) | ||||
|       }) | ||||
|     }, | ||||
|     showQR() { | ||||
|       if (!this.qrCode) { | ||||
|         let url = `${location.href}?userId=${this.user.info.id}#phone` | ||||
|         this.instance.post("/app/syssignaccount/draw-qrcode", null, { | ||||
|           params: {url} | ||||
|         }).then(res => { | ||||
|           if (res?.data) { | ||||
|             this.showQRCode = true | ||||
|             this.qrCode = res.data | ||||
|           } | ||||
|         }) | ||||
|       } else { | ||||
|         this.showQRCode = !this.showQRCode | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .personal-signature { | ||||
|   height: 100%; | ||||
|   background: #f3f6f9; | ||||
|   overflow: auto; | ||||
|  | ||||
|   :deep( .signaturePane ){ | ||||
|     display: flex; | ||||
|     gap: 16px; | ||||
|     flex-wrap: wrap; | ||||
|     padding: 16px; | ||||
|  | ||||
|     .signatureCard { | ||||
|       width: 290px; | ||||
|       height: 258px; | ||||
|       background: #FFFFFF; | ||||
|       border-radius: 4px; | ||||
|       position: relative; | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       overflow: hidden; | ||||
|  | ||||
|       &.add { | ||||
|         justify-content: center; | ||||
|         align-items: center; | ||||
|         color: #666666; | ||||
|         cursor: pointer; | ||||
|  | ||||
|         .AiIcon { | ||||
|           width: 32px; | ||||
|           height: 32px; | ||||
|           font-size: 32px; | ||||
|         } | ||||
|  | ||||
|         & > span { | ||||
|           font-size: 12px; | ||||
|           line-height: 16px; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .default { | ||||
|         position: absolute; | ||||
|         width: 56px; | ||||
|         height: 24px; | ||||
|         background: #3573FF; | ||||
|         border-radius: 0 0 4px 0; | ||||
|         top: 0; | ||||
|         left: 0; | ||||
|         text-align: center; | ||||
|         line-height: 24px; | ||||
|         font-size: 12px; | ||||
|         color: #FFF; | ||||
|       } | ||||
|  | ||||
|       .body { | ||||
|         min-height: 0; | ||||
|         flex: 1; | ||||
|         display: flex; | ||||
|         justify-content: center; | ||||
|         align-items: center; | ||||
|         padding: 25px; | ||||
|         pointer-events: none; | ||||
|  | ||||
|         .el-image { | ||||
|           width: 100%; | ||||
|           height: 100% | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .footer { | ||||
|         flex-shrink: 0; | ||||
|         height: 40px; | ||||
|         background: rgba(#30426F, .5); | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         padding: 8px 0; | ||||
|         box-sizing: border-box; | ||||
|  | ||||
|         hr { | ||||
|           height: 100%; | ||||
|           border-color: rgba(#fff, .5); | ||||
|         } | ||||
|  | ||||
|         & > .el-button { | ||||
|           flex: 1; | ||||
|           color: #fff; | ||||
|  | ||||
|           &[disabled] { | ||||
|             color: rgba(#fff, .5); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   :deep( .writeInPhone ){ | ||||
|     position: absolute; | ||||
|     width: 100px; | ||||
|     height: 32px; | ||||
|     background: rgba(#000, .5); | ||||
|     border-radius: 16px; | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|     gap: 4px; | ||||
|     color: rgba(#fff, .6); | ||||
|     cursor: pointer; | ||||
|     left: 16px; | ||||
|     top: 16px; | ||||
|  | ||||
|     .AiIcon { | ||||
|       width: auto; | ||||
|       height: auto; | ||||
|     } | ||||
|  | ||||
|     &:hover { | ||||
|       color: #fff; | ||||
|     } | ||||
|   } | ||||
|   :deep( .ai-dialog__wrapper ){ | ||||
|     .ai-dialog__content--wrapper { | ||||
|       padding-right: 0 !important; | ||||
|     } | ||||
|  | ||||
|     .el-dialog__body { | ||||
|       padding: 24px 0; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     .authZone { | ||||
|       padding: 0 16px 24px; | ||||
|       width: 100%; | ||||
|       box-sizing: border-box; | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       gap: 24px; | ||||
|  | ||||
|       .el-alert { | ||||
|         border: 1px solid #FF8822; | ||||
|       } | ||||
|  | ||||
|       .el-form-item { | ||||
|         margin-bottom: 0; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -1,110 +0,0 @@ | ||||
| <template> | ||||
|   <section class="drawInPhone" @touchmove.prevent> | ||||
|     <div class="endPage" v-if="finished">操作结束请关闭页面</div> | ||||
|     <ai-drawer :seal.sync="sealData" placeholder="请签名" :width="device.width" :height="device.height"> | ||||
|       <template #tools> | ||||
|         <div class="writeInPhone" slot="reference" @click.stop="handleSubmit"> | ||||
|           <ai-icon icon="iconPublish"/> | ||||
|           <span>提交</span> | ||||
|         </div> | ||||
|       </template> | ||||
|     </ai-drawer> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "drawInPhone", | ||||
|   inject: ['signature'], | ||||
|   data() { | ||||
|     return { | ||||
|       sealData: null, | ||||
|       device: {width: 0, height: 0}, | ||||
|       finished: false | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.device.width = document.body.clientWidth | ||||
|     this.device.height = document.body.clientHeight | ||||
|     window.onresize = () => { | ||||
|       this.device.width = document.body.clientWidth | ||||
|       this.device.height = document.body.clientHeight | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     handleSubmit() { | ||||
|       let sealData = this.sealData?.replace(/data:image\/png;base64,/, ''), | ||||
|           {userId} = this.$route.query | ||||
|       if (!userId) return alert("缺少必要参数") | ||||
|       sealData && this.signature.instance({ | ||||
|         url: '/app/syssignaccount/upload-sealdata', | ||||
|         headers: {"Content-Type": "application/json"}, | ||||
|         method: 'post', | ||||
|         params: {userId}, | ||||
|         data: sealData, | ||||
|         withoutToken: true | ||||
|       }).then(res => { | ||||
|         if (res?.code == 0) { | ||||
|           alert("添加成功!") | ||||
|           this.finished = true | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .drawInPhone { | ||||
|   position: fixed; | ||||
|   left: 0; | ||||
|   right: 0; | ||||
|   top: 0; | ||||
|   bottom: 0; | ||||
|   z-index: 20210205932; | ||||
|  | ||||
|   .endPage { | ||||
|     position: fixed; | ||||
|     left: 0; | ||||
|     right: 0; | ||||
|     top: 0; | ||||
|     bottom: 0; | ||||
|     z-index: 20210205933; | ||||
|     background: rgba(#000, .8); | ||||
|     color: #999; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
|     font-size: 32px; | ||||
|   } | ||||
|  | ||||
|   .AiDrawer { | ||||
|     margin: 0; | ||||
|   } | ||||
|  | ||||
|   .writeInPhone { | ||||
|     position: absolute; | ||||
|     width: 72px; | ||||
|     height: 32px; | ||||
|     background: rgba(#000, .5); | ||||
|     border-radius: 16px; | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|     gap: 4px; | ||||
|     color: rgba(#fff, .6); | ||||
|     cursor: pointer; | ||||
|     left: 16px; | ||||
|     top: 16px; | ||||
|  | ||||
|     .AiIcon { | ||||
|       width: auto; | ||||
|       height: auto; | ||||
|     } | ||||
|  | ||||
|     &:hover { | ||||
|       color: #fff; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -1,66 +0,0 @@ | ||||
| <template> | ||||
|   <div class="doc-circulation ailist-wrapper"> | ||||
|     <keep-alive :include="['List']"> | ||||
|       <component ref="component" :is="component" @change="onChange" :params="params" :instance="instance" :dict="dict"></component> | ||||
|     </keep-alive> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import List from './components/List' | ||||
|   import Add from './components/Add' | ||||
|  | ||||
|   export default { | ||||
|     name: 'AppBuddyMessage', | ||||
|     label: '好友欢迎语', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       return { | ||||
|         component: 'List', | ||||
|         params: {}, | ||||
|         include: [] | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     components: { | ||||
|       Add, | ||||
|       List | ||||
|     }, | ||||
|  | ||||
|     mounted () { | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       onChange (data) { | ||||
|         if (data.type === 'Add') { | ||||
|           this.component = 'Add' | ||||
|           this.params = data.params | ||||
|         } | ||||
|  | ||||
|         if (data.type === 'list') { | ||||
|           this.component = 'List' | ||||
|           this.params = data.params | ||||
|  | ||||
|           this.$nextTick(() => { | ||||
|             if (data.isRefresh) { | ||||
|               this.$refs.component.getList() | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss"> | ||||
|   .doc-circulation { | ||||
|     height: 100%; | ||||
|     background: #F3F6F9; | ||||
|     overflow: auto; | ||||
|   } | ||||
| </style> | ||||
| @@ -1,380 +0,0 @@ | ||||
| <template> | ||||
|   <ai-detail> | ||||
|     <template slot="title"> | ||||
|       <ai-title title="新建欢迎语" isShowBack isShowBottomBorder @onBackClick="cancel(false)"></ai-title> | ||||
|     </template> | ||||
|  | ||||
|     <template slot="content"> | ||||
|       <ai-card> | ||||
|         <template #title> | ||||
|           <div class="ai-card__title"> | ||||
|             <h2>基本信息</h2> | ||||
|             <span>*一个成员如果被设置了多个欢迎语,将会使用最新设置或修改的欢迎语</span> | ||||
|           </div> | ||||
|         </template> | ||||
|  | ||||
|         <template #content> | ||||
|           <el-form class="ai-form" :rules="rules" ref="userForm" :model="form" label-width="100px" label-position="right"> | ||||
|             <el-form-item label="使用成员" prop="users" style="width: 100%"> | ||||
|               <el-input v-model="users" placeholder="请选择使用成员" disabled style="width: 100%"> | ||||
|                 <template #append> | ||||
|                   <ai-user-selecter refs="addTags" :instance="instance" v-model="form.users"> | ||||
|                     <el-button size="small">选择</el-button> | ||||
|                   </ai-user-selecter> | ||||
|                 </template> | ||||
|               </el-input> | ||||
|             </el-form-item> | ||||
|           </el-form> | ||||
|         </template> | ||||
|       </ai-card> | ||||
|  | ||||
|       <ai-card title="发送欢迎语"> | ||||
|         <template #content> | ||||
|           <el-form class="ai-form" ref="form" :model="form" label-width="110px" label-position="right"> | ||||
|             <el-form-item class="el-form-item__textarea" label="文本内容" prop="explain" style="width: 100%"> | ||||
|               <span @click="insertNickname" class="el-form-item__btn" type="text">[插入居民昵称]</span> | ||||
|               <el-input type="textarea" placeholder="请输入…" v-model="form.content" maxlength="1000" :rows="5" show-word-limit></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="其他类型" prop="explain" style="width: 100%"> | ||||
|               <el-radio-group v-model="form.type" @change="onTypeChange"> | ||||
|                 <el-radio label="image">图片</el-radio> | ||||
|                 <el-radio label="link">链接</el-radio> | ||||
|                 <el-radio label="video">视频</el-radio> | ||||
|                 <el-radio label="miniapp">小程序</el-radio> | ||||
|               </el-radio-group> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="图片" prop="files" style="width: 100%" v-if="form.type === 'image'"> | ||||
|               <ai-uploader :instance="instance" isWechat v-model="form.files" :limit="1" url="/app/wxcp/upload/uploadFile?type=image"></ai-uploader> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="链接" prop="linkUrl" style="width: 100%" v-if="form.type === 'link'"> | ||||
|               <el-input placeholder="链接地址请以http或https开头" :rows="2" type="textarea" v-model="form.linkUrl"></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="链接图片地址" prop="picUrl" style="width: 100%" v-if="form.type === 'link'"> | ||||
|               <ai-uploader :instance="instance" v-model="form.picUrl" :limit="1"></ai-uploader> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="链接标题" prop="title" style="width: 100%" v-if="form.type === 'link'"> | ||||
|               <el-input placeholder="请输入链接标题" v-model="form.title"></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="视频" prop="files" style="width: 100%" v-if="form.type === 'video'"> | ||||
|               <ai-uploader :instance="instance" fileType="file" isWechat acceptType=".mp4" v-model="form.files" :limit="1" url="/app/wxcp/upload/uploadFile?type=video"></ai-uploader> | ||||
|             </el-form-item> | ||||
|             <!-- <el-form-item label="小程序" prop="applets" style="width: 100%" v-if="form.type === 'miniapp'"> | ||||
|               <el-radio-group v-model="form.applets"> | ||||
|                 <div class="appletss"> | ||||
|                   <div class="applets-item" @click="form.applets = '0'" :class="[form.applets === '0' ? 'applets-active' : '']"> | ||||
|                     <el-radio label="0"></el-radio> | ||||
|                     <img src="http://www.9665.com/uploadfile/2018/0607/20180607042142312.png"> | ||||
|                     <span>小程序</span> | ||||
|                   </div> | ||||
|                   <div class="applets-item" @click="form.applets = '1'" :class="[form.applets === '1' ? 'applets-active' : '']"> | ||||
|                     <el-radio label="1"></el-radio> | ||||
|                     <img src="http://www.9665.com/uploadfile/2018/0607/20180607042142312.png"> | ||||
|                     <span>小程序</span> | ||||
|                   </div> | ||||
|                   <div class="applets-item" @click="form.applets = '2'" :class="[form.applets === '2' ? 'applets-active' : '']"> | ||||
|                     <el-radio label="2"></el-radio> | ||||
|                     <img src="http://www.9665.com/uploadfile/2018/0607/20180607042142312.png"> | ||||
|                     <span>小程序</span> | ||||
|                   </div> | ||||
|                 </div> | ||||
|               </el-radio-group> | ||||
|             </el-form-item> --> | ||||
|             <el-form-item label="小程序标题" prop="title" style="width: 100%" v-if="form.type === 'miniapp'"> | ||||
|               <el-input placeholder="请输入小程序标题" v-model="form.title"></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="小程序APPID" prop="appid" style="width: 100%" v-if="form.type === 'miniapp'"> | ||||
|               <el-input placeholder="请输入小程序APPID" v-model="form.appid"></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="小程序跳转页面" prop="page" style="width: 100%" v-if="form.type === 'miniapp'"> | ||||
|               <el-input placeholder="如pages/home/Home" v-model="form.page"></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="小程序图片" prop="files" style="width: 100%" v-if="form.type === 'miniapp'"> | ||||
|               <ai-uploader :instance="instance" v-model="form.files" isWechat :limit="1" url="/app/wxcp/upload/uploadFile?type=image"></ai-uploader> | ||||
|             </el-form-item> | ||||
|           </el-form> | ||||
|         </template> | ||||
|       </ai-card> | ||||
|     </template> | ||||
|  | ||||
|     <template #footer> | ||||
|       <el-button @click="cancel">取消</el-button> | ||||
|       <el-button type="primary" @click="confirm">提交</el-button> | ||||
|     </template> | ||||
|   </ai-detail> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   const validateUser = (rule, value, callback) => { | ||||
|     if (!value.length) { | ||||
|       callback(new Error('请选择使用成员')) | ||||
|     } else { | ||||
|       callback() | ||||
|     } | ||||
|   } | ||||
|   export default { | ||||
|     name: 'Add', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object, | ||||
|       params: Object | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       return { | ||||
|         isShow: false, | ||||
|         info: {}, | ||||
|         form: { | ||||
|           users: [], | ||||
|           appId: '', | ||||
|           page: '', | ||||
|           title: '', | ||||
|           miniappImg: [], | ||||
|           picUrl: [], | ||||
|           content: '', | ||||
|           files: [], | ||||
|           linkUrl: '', | ||||
|           isRemind: true, | ||||
|           type: 'text' | ||||
|         }, | ||||
|         rules: { | ||||
|           users: [ | ||||
|             { required: true, message: '请选择使用成员', trigger: 'change' }, | ||||
|             { validator: validateUser, trigger: 'change' } | ||||
|           ] | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|       users () { | ||||
|         return this.form.users.map(v => v.name).join(',') | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     created () { | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       getInfo (id) { | ||||
|         this.instance.post(`/app/appleavemessage/queryDetailById?id=${id}`).then(res => { | ||||
|           if (res.code === 0) { | ||||
|             this.info = res.data | ||||
|             this.info.appLeaveMessageReplyList = res.data.appLeaveMessageReplyList.map(item => { | ||||
|               item.images = JSON.parse(item.images).map(item => { | ||||
|                 return { | ||||
|                   ...item, | ||||
|                   url: item.accessUrl | ||||
|                 } | ||||
|               }) | ||||
|  | ||||
|               return item | ||||
|             }) | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       validateUser (rule, value, callback) { | ||||
|         if (!value.length) { | ||||
|           callback(new Error('请选择使用成员')) | ||||
|         } else { | ||||
|           callback() | ||||
|         } | ||||
|       }, | ||||
|  | ||||
|       onClose () { | ||||
|         this.form.explain = '' | ||||
|       }, | ||||
|  | ||||
|       onTypeChange () { | ||||
|         this.form.files = [] | ||||
|         this.form.picUrl = [] | ||||
|         this.form.appId = '' | ||||
|         this.form.page = '' | ||||
|         this.form.title = '' | ||||
|         this.form.miniappImg = [] | ||||
|         this.form.linkUrl = '' | ||||
|       }, | ||||
|  | ||||
|       insertNickname () { | ||||
|         this.form.content = this.form.content + '[用户昵称]' | ||||
|       }, | ||||
|  | ||||
|       onChange () { | ||||
|  | ||||
|       }, | ||||
|  | ||||
|       confirm () { | ||||
|         this.$refs.userForm.validate((valid) => { | ||||
|           if (valid) { | ||||
|             if (this.form.type === 'text' && !this.form.content) { | ||||
|               return this.$message.error('请输入消息内容') | ||||
|             } | ||||
|  | ||||
|             if (this.form.type === 'image' && !this.form.files.length) { | ||||
|               return this.$message.error('请上传图片') | ||||
|             } | ||||
|             // if (this.form.type === 'file' && !this.form.files.length) { | ||||
|             //   return this.$message.error('请上传附件') | ||||
|             // } | ||||
|             if (this.form.type === 'video' && !this.form.files.length) { | ||||
|               return this.$message.error('请上传视频') | ||||
|             } | ||||
|             if (this.form.type === 'link' && !this.form.linkUrl) { | ||||
|               return this.$message.error('请输入链接') | ||||
|             } | ||||
|             if (this.form.type === 'link' && !this.form.picUrl.length) { | ||||
|               return this.$message.error('请输入链接图片') | ||||
|             } | ||||
|             if (this.form.type === 'link' && !this.form.title) { | ||||
|               return this.$message.error('请输入链接标题') | ||||
|             } | ||||
|             if (this.form.type === 'miniapp' && !this.form.title) { | ||||
|               return this.$message.error('请输入小程序标题') | ||||
|             } | ||||
|             if (this.form.type === 'miniapp' && !this.form.appid) { | ||||
|               return this.$message.error('请输入小程序appid') | ||||
|             } | ||||
|             if (this.form.type === 'miniapp' && !this.form.page) { | ||||
|               return this.$message.error('请输入小程序page') | ||||
|             } | ||||
|             if (this.form.type === 'miniapp' && !this.form.files.length) { | ||||
|               return this.$message.error('请上传小程序图片') | ||||
|             } | ||||
|  | ||||
|             this.instance.post(`/app/wxcp/wxwelcomeword/add`, { | ||||
|               type: '1', | ||||
|               content: this.form.content || '', | ||||
|               isNotify: '0', | ||||
|               media: { | ||||
|                 createdAt: this.form.files.length ? this.form.files[0].media.createdAt : '', | ||||
|                 file: this.form.files.length ? this.form.files[0] : {}, | ||||
|                 mediaId: this.form.files.length ? this.form.files[0].media.mediaId : '', | ||||
|                 sysFileId: this.form.files.length ? this.form.files[0].id : '', | ||||
|                 type: this.form.type, | ||||
|                 linkUrl: this.form.type === 'link' && this.form.linkUrl ? this.form.linkUrl : '', | ||||
|                 title: this.form.title, | ||||
|                 appId: this.form.appid, | ||||
|                 page: this.form.page, | ||||
|                 picUrl: this.form.type === 'link' ? this.form.picUrl[0].url : '' | ||||
|               }, | ||||
|               users: this.form.users.map(item => { | ||||
|                 return { | ||||
|                   type: 0, | ||||
|                   objectId: item.id, | ||||
|                   objectName: item.name | ||||
|                 } | ||||
|               }) | ||||
|             }).then(res => { | ||||
|               if (res.code == 0) { | ||||
|                 this.$message.success('提交成功') | ||||
|                 setTimeout(() => { | ||||
|                   this.cancel(true) | ||||
|                 }, 600) | ||||
|               } | ||||
|             }) | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       cancel (isRefresh) { | ||||
|         this.$emit('change', { | ||||
|           type: 'list', | ||||
|           isRefresh: isRefresh ? true : false | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
|   .ai-card__title { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|  | ||||
|     h2 { | ||||
|       margin-right: 20px; | ||||
|       color: #222222; | ||||
|       font-size: 16px; | ||||
|       font-weight: 700; | ||||
|     } | ||||
|  | ||||
|     span { | ||||
|       color: #888888; | ||||
|       font-size: 14px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .appletss { | ||||
|     display: flex; | ||||
|     flex-wrap: wrap; | ||||
|  | ||||
|     .applets-item { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       width: 400px; | ||||
|       height: 60px; | ||||
|       margin-right: 8px; | ||||
|       margin-bottom: 8px; | ||||
|       padding: 0 17px; | ||||
|       background: #FFFFFF; | ||||
|       border-radius: 2px; | ||||
|       border: 1px solid #D0D4DC; | ||||
|       cursor: pointer; | ||||
|  | ||||
|       &:hover { | ||||
|         border-color: #2266FF; | ||||
|       } | ||||
|  | ||||
|       :deep( ){ | ||||
|         .el-radio__label { | ||||
|           display: none; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       img { | ||||
|         width: 40px; | ||||
|         height: 40px; | ||||
|         margin: 0 8px 0 13px; | ||||
|       } | ||||
|  | ||||
|       span { | ||||
|         color: #222222; | ||||
|         font-size: 12px; | ||||
|       } | ||||
|  | ||||
|       .el-radio { | ||||
|         margin: 0; | ||||
|       } | ||||
|  | ||||
|       &.applets-active { | ||||
|         border-color: #2266FF; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .tips { | ||||
|     position: relative; | ||||
|     top: 2px; | ||||
|     padding-left: 8px; | ||||
|     color: #222222; | ||||
|     font-size: 14px; | ||||
|   } | ||||
|  | ||||
|   .el-form-item-item__textarea  { | ||||
|     position: relative; | ||||
|   } | ||||
|  | ||||
|     .el-form-item__btn { | ||||
|       position: absolute; | ||||
|       bottom: 12px; | ||||
|       left: 12px; | ||||
|       line-height: 1; | ||||
|       z-index: 1; | ||||
|       color: #2266FF; | ||||
|       font-size: 14px; | ||||
|       user-select: none; | ||||
|       background: #fff; | ||||
|       cursor: pointer; | ||||
|     } | ||||
| </style> | ||||
| @@ -1,235 +0,0 @@ | ||||
| <template> | ||||
|   <ai-list class="message"> | ||||
|     <template slot="title"> | ||||
|       <ai-title title="好友欢迎语" isShowBottomBorder></ai-title> | ||||
|     </template> | ||||
|     <template slot="content"> | ||||
|       <ai-search-bar class="search-bar"> | ||||
|         <template #left> | ||||
|           <el-button size="small" type="primary" icon="iconfont iconAdd" @click="toAdd">添加好友欢迎语</el-button> | ||||
|         </template> | ||||
|         <template slot="right"> | ||||
|         </template> | ||||
|       </ai-search-bar> | ||||
|       <ai-table | ||||
|         :tableData="tableData" | ||||
|         :col-configs="colConfigs" | ||||
|         :total="total" | ||||
|         style="margin-top: 6px;" | ||||
|         :current.sync="search.current" | ||||
|         :size.sync="search.size" | ||||
|         @getList="getList"> | ||||
|         <el-table-column slot="type" width="240px" label="消息内容" align="center"> | ||||
|           <template slot-scope="{ row }"> | ||||
|             <el-popover | ||||
|               placement="bottom" | ||||
|               width="400" | ||||
|               :visible-arrow="false" | ||||
|               popper-class="wechat-message__container" | ||||
|               trigger="hover"> | ||||
|               <div class="count" slot="reference">共{{ row.count }}条</div> | ||||
|               <div class="message-info"> | ||||
|                 <h2 v-if="row.content" :style="{marginBottom: row.media ? '16px' : '0'}">{{ row.content }}</h2> | ||||
|                 <div class="message-info__wrapper" v-if="row.media && (row.media.file || row.media.type === 'link')"> | ||||
|                   <img v-if="row.media.type === 'image' || row.media.type === 'miniapp'" :src="row.media.file.url"> | ||||
|                   <video v-if="row.media.type === 'video'" :src="row.media.file.url"></video> | ||||
|                   <img v-if="row.media.type === 'link'" :src="row.media.picUrl"> | ||||
|                   <div class="message-info__wrapper--right"> | ||||
|                     <h3 v-if="row.media.type === 'miniapp'">{{ row.media.title }}</h3> | ||||
|                     <h3 v-if="row.media.type === 'image'">{{ row.media.file.name }}</h3> | ||||
|                     <h3 v-if="row.media.type === 'link'">{{ row.media.linkUrl }}</h3> | ||||
|                     <h3 v-if="row.media.type === 'video'">{{ row.media.file.name }}</h3> | ||||
|                     <p v-if="row.media.type === 'image'">{{ row.media.file.fileSizeStr }}</p> | ||||
|                     <p v-if="row.media.type === 'link'">{{ row.media.title }}</p> | ||||
|                     <p v-if="row.media.type === 'video'">{{ row.media.file.fileSizeStr }}</p> | ||||
|                   </div> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </el-popover> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column slot="options" width="120px" fixed="right" label="操作" align="center"> | ||||
|           <template slot-scope="{ row }"> | ||||
|             <div class="table-options"> | ||||
|               <!-- <el-button type="text" disabled @click="toAdd(row.id)" title="编辑">编辑</el-button> --> | ||||
|               <el-button type="text" @click="remove(row.id)" title="删除">删除</el-button> | ||||
|             </div> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|       </ai-table> | ||||
|     </template> | ||||
|   </ai-list> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   export default { | ||||
|     name: 'List', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       return { | ||||
|         search: { | ||||
|           current: 1, | ||||
|           size: 10, | ||||
|           content: '' | ||||
|         }, | ||||
|         currIndex: 0, | ||||
|         total: 10, | ||||
|         colConfigs: [ | ||||
|           { prop: 'type',  label: '类型', align: 'left', width: '160', | ||||
|             render: (h, params) => { | ||||
|               let str = '' | ||||
|  | ||||
|               if (params.row.content) { | ||||
|                 str = '文字' | ||||
|               } | ||||
|  | ||||
|               if (params.row.media && params.row.media.type === 'link') { | ||||
|                 str += this.dict.getLabel('wxMsgType', params.row.media.type) ? (params.row.content ? '+' : '') + this.dict.getLabel('wxMsgType', params.row.media.type) : '' | ||||
|               } | ||||
|  | ||||
|               if (params.row.media && params.row.media.file) { | ||||
|                 str += this.dict.getLabel('wxMsgType', params.row.media.type) ? (params.row.content ? '+' : '') + this.dict.getLabel('wxMsgType', params.row.media.type) : '' | ||||
|               } | ||||
|  | ||||
|               return h('span', { | ||||
|                 style: { | ||||
|                 } | ||||
|               }, str) | ||||
|             } | ||||
|           }, | ||||
|           { slot: 'type' }, | ||||
|           { prop: 'users', label: '使用成员', align: 'left', format: v => v.join(';') }, | ||||
|           { prop: 'createUser', label: '创建人' }, | ||||
|           { prop: 'createTime', label: '编辑时间' }, | ||||
|           { slot: 'options', label: '操作' } | ||||
|         ], | ||||
|         tableData: [] | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     mounted () { | ||||
|       this.dict.load(['wxMsgType']).then(() => { | ||||
|         this.getList() | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       getList() { | ||||
|         this.instance.post(`/app/wxcp/wxwelcomeword/list`, null, { | ||||
|           params: { | ||||
|             ...this.search | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.tableData = res.data.records.map(item => { | ||||
|               let count = 0 | ||||
|  | ||||
|               if (item.content) { | ||||
|                 count = count + 1 | ||||
|               } | ||||
|  | ||||
|               if (item.media && item.media.type !== 'text') { | ||||
|                 count = count + 1 | ||||
|               } | ||||
|  | ||||
|               item.count = count | ||||
|               return item | ||||
|             }) | ||||
|             this.total = res.data.total | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       remove (id) { | ||||
|         this.$confirm('确定删除该数据?').then(() => { | ||||
|           this.instance.post(`/app/wxcp/wxwelcomeword/delete?id=${id}`).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               this.$message.success('删除成功!') | ||||
|               this.getList() | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       toAdd (id) { | ||||
|         this.$emit('change', { | ||||
|           type: 'Add', | ||||
|           params: { | ||||
|             id | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .message { | ||||
|     .count { | ||||
|       cursor: pointer; | ||||
|       color: #2266FF; | ||||
|       font-size: 14px; | ||||
|  | ||||
|       &:hover { | ||||
|         opacity: 0.6; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .message-info { | ||||
|     padding: 8px; | ||||
|     min-height: 116px; | ||||
|  | ||||
|     h2 { | ||||
|       color: #222222; | ||||
|       font-weight: 500; | ||||
|       font-size: 14px; | ||||
|     } | ||||
|  | ||||
|     .message-info__wrapper { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       width: 368px; | ||||
|       height: 60px; | ||||
|       padding: 10px; | ||||
|       background: #FFFFFF; | ||||
|       border-radius: 2px; | ||||
|       border: 1px solid #D0D4DC; | ||||
|  | ||||
|       .message-info__wrapper--right { | ||||
|         flex: 1; | ||||
|         overflow: hidden; | ||||
|         text-overflow:ellipsis; | ||||
|         white-space: nowrap; | ||||
|       } | ||||
|  | ||||
|       h3 { | ||||
|         width: 100%; | ||||
|         color: #222222; | ||||
|         font-size: 14px; | ||||
|         overflow: hidden; | ||||
|         text-overflow:ellipsis; | ||||
|         white-space: nowrap; | ||||
|         font-weight: normal; | ||||
|       } | ||||
|  | ||||
|       img, video { | ||||
|         width: 40px; | ||||
|         height: 40px; | ||||
|         margin-right: 10px; | ||||
|         object-fit: cover; | ||||
|       } | ||||
|  | ||||
|       p { | ||||
|         margin-top: 6px; | ||||
|         font-size: 14px; | ||||
|         color: #888888; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
| @@ -1,47 +0,0 @@ | ||||
| <template> | ||||
|   <div class="doc-circulation ailist-wrapper"> | ||||
|     <keep-alive :include="['List']"> | ||||
|       <component ref="component" :is="component" :instance="instance" :dict="dict"></component> | ||||
|     </keep-alive> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import List from './components/List' | ||||
|  | ||||
|   export default { | ||||
|     name: 'AppMaterialLibrary', | ||||
|     label: '素材库', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       return { | ||||
|         component: 'List', | ||||
|         params: {}, | ||||
|         include: [] | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     components: { | ||||
|       List | ||||
|     }, | ||||
|  | ||||
|     mounted () { | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss"> | ||||
|   .doc-circulation { | ||||
|     height: 100%; | ||||
|     background: #F3F6F9; | ||||
|     overflow: auto; | ||||
|   } | ||||
| </style> | ||||
| @@ -1,212 +0,0 @@ | ||||
| <template> | ||||
|   <div id="ChooseMaterial"> | ||||
|     <ai-dialog | ||||
|       :visible.sync="isShow" | ||||
|       width="890px" | ||||
|       @onConfirm="onConfirm" | ||||
|       title="选择素材"> | ||||
|       <div class="AppMaterialLibrary-title"> | ||||
|         <span | ||||
|           v-for="(item, index) in typeList" | ||||
|           :key="index" | ||||
|           :class="[currIndex === index ? 'active' : '']" @click="currIndex = index, search.current = 1, getList()"> | ||||
|           {{ item }} | ||||
|         </span> | ||||
|       </div> | ||||
|       <ai-search-bar class="search-bar"> | ||||
|         <template #left> | ||||
|         </template> | ||||
|         <template slot="right"> | ||||
|           <el-input | ||||
|             v-model="search.title" | ||||
|             size="small" | ||||
|             v-throttle="() => { search.current = 1, getList() }" | ||||
|             placeholder="请输入标题、话术内容、添加人" | ||||
|             clearable | ||||
|             @clear="search.current = 1, search.title = '', getList()" | ||||
|             suffix-icon="iconfont iconSearch"> | ||||
|           </el-input> | ||||
|         </template> | ||||
|       </ai-search-bar> | ||||
|       <ai-table | ||||
|         :tableData="tableData" | ||||
|         :col-configs="colConfigs" | ||||
|         :total="total" | ||||
|         style="margin-top: 6px; width: 100%;" | ||||
|         :current.sync="search.current" | ||||
|         :size.sync="search.size" | ||||
|         @selection-change="v=> ids = v.map((e) => e.id)" | ||||
|         @getList="getList"> | ||||
|       </ai-table> | ||||
|     </ai-dialog> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   export default { | ||||
|     name: 'ChooseMaterial', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function | ||||
|     }, | ||||
|  | ||||
|     data() { | ||||
|       return { | ||||
|         search: { | ||||
|           current: 1, | ||||
|           size: 10, | ||||
|           title: '', | ||||
|         }, | ||||
|         ids: [], | ||||
|         isShow: false, | ||||
|         id: '', | ||||
|         typeList: ['话术', '图片', '小程序', '文件', '视频', '网页'], | ||||
|         currIndex: 0, | ||||
|         tableData: [], | ||||
|         total: 0 | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|       mpTitle () { | ||||
|         return { | ||||
|           '0': '话术标题', | ||||
|           '1': '图片名称', | ||||
|           '2': '小程序标题', | ||||
|           '3': '文件名称', | ||||
|           '4': '视频名称', | ||||
|           '5': '网页名称' | ||||
|         }[this.currIndex] | ||||
|       }, | ||||
|  | ||||
|       colConfigs () { | ||||
|         if (this.currIndex === 0) { | ||||
|           return [ | ||||
|             { type: 'selection' }, | ||||
|             { prop: 'title', label: this.mpTitle }, | ||||
|             { prop: 'content', label: '话术内容', align: 'center' }, | ||||
|             { prop: 'createUserName', label: '添加人', align: 'center' }, | ||||
|             { prop: 'createTime', label: '添加时间', align: 'center' } | ||||
|           ] | ||||
|         } | ||||
|  | ||||
|         if (this.currIndex === 2) { | ||||
|           return [ | ||||
|             { type: 'selection' }, | ||||
|             { prop: 'title', label: this.mpTitle }, | ||||
|             { prop: 'appId', label: '小程序APPID', align: 'center' }, | ||||
|             { prop: 'createUserName', label: '添加人', align: 'center' }, | ||||
|             { prop: 'createTime', label: '添加时间', align: 'center' } | ||||
|           ] | ||||
|         } | ||||
|  | ||||
|         if (this.currIndex === 5) { | ||||
|           return [ | ||||
|             { type: 'selection' }, | ||||
|             { prop: 'title', label: this.mpTitle }, | ||||
|             { prop: 'pagePath', label: '外链网页', align: 'center' }, | ||||
|             { prop: 'createUserName', label: '添加人', align: 'center' }, | ||||
|             { prop: 'createTime', label: '添加时间', align: 'center' } | ||||
|           ] | ||||
|         } | ||||
|  | ||||
|         return [ | ||||
|           { type: 'selection' }, | ||||
|           { prop: 'title', label: this.mpTitle }, | ||||
|           { prop: 'fileSizeStr', label: '文件大小', align: 'center' }, | ||||
|           { prop: 'createUserName', label: '添加人', align: 'center' }, | ||||
|           { prop: 'createTime', label: '添加时间', align: 'center' } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     created () { | ||||
|       this.getList() | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       getList() { | ||||
|         this.instance.post(`/app/appmaterialinfo/list`, null, { | ||||
|           params: { | ||||
|             ...this.search, | ||||
|             type: this.currIndex | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.tableData = res.data.records | ||||
|             this.total = res.data.total | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       onConfirm () { | ||||
|         if (!this.ids.length) { | ||||
|           return this.$message.error('请选择素材') | ||||
|         } | ||||
|  | ||||
|         if (this.ids.length > 1) { | ||||
|           return this.$message.error('素材不能多选') | ||||
|         } | ||||
|  | ||||
|  | ||||
|       }, | ||||
|  | ||||
|       open () { | ||||
|         this.isShow = true | ||||
|       }, | ||||
|  | ||||
|       close () { | ||||
|         this.isShow = false | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   #ChooseMaterial { | ||||
|     :deep( .ai-list__content--right-wrapper ) { | ||||
|       padding: 0 20px!important; | ||||
|     } | ||||
|  | ||||
|     :deep( .el-dialog__header ) { | ||||
|       display: none; | ||||
|     } | ||||
|  | ||||
|     :deep( .el-dialog__body ) { | ||||
|       padding-top: 0!important; | ||||
|     } | ||||
|  | ||||
|     .AppMaterialLibrary-title { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       margin-bottom: 20px; | ||||
|       border-bottom: 1px solid #eee; | ||||
|  | ||||
|       span { | ||||
|         height: 100%; | ||||
|         line-height: 56px; | ||||
|         margin-right: 32px; | ||||
|         color: #888888; | ||||
|         font-size: 16px; | ||||
|         font-weight: 600; | ||||
|         transition: all ease 0.3s; | ||||
|         border-bottom: 3px solid transparent; | ||||
|         cursor: pointer; | ||||
|         user-select: none; | ||||
|  | ||||
|         &:hover { | ||||
|           color: #222; | ||||
|         } | ||||
|  | ||||
|         &:last-child { | ||||
|           margin-right: 0; | ||||
|         } | ||||
|  | ||||
|         &.active { | ||||
|           color: #222222; | ||||
|           border-bottom: 3px solid #2266FF; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
| @@ -1,394 +0,0 @@ | ||||
| <template> | ||||
|   <ai-list id="AppMaterialLibrary"> | ||||
|     <template slot="title"> | ||||
|       <ai-title title="素材管理" isShowBottomBorder> | ||||
|         <template #sub> | ||||
|           <span>可在聊天素材、宣发工具中选择素材发送给居民,提升服务效率;其中文件、视频和网页素材支持记录居民查看行为</span> | ||||
|         </template> | ||||
|       </ai-title> | ||||
|     </template> | ||||
|     <template slot="content"> | ||||
|       <div class="AppMaterialLibrary-title"> | ||||
|         <span | ||||
|           v-for="(item, index) in typeList" | ||||
|           :key="index" | ||||
|           :class="[currIndex === index ? 'active' : '']" @click="currIndex = index, search.current = 1, getList()"> | ||||
|           {{ item }} | ||||
|         </span> | ||||
|       </div> | ||||
|       <ai-search-bar class="search-bar"> | ||||
|         <template #left> | ||||
|           <el-button size="small" type="primary" icon="iconfont iconAdd" @click="isShow = true">添加{{ typeList[currIndex] }}</el-button> | ||||
|         </template> | ||||
|         <template slot="right"> | ||||
|           <el-input | ||||
|             v-model="search.title" | ||||
|             size="small" | ||||
|             v-throttle="() => { search.current = 1, getList() }" | ||||
|             placeholder="请输入标题、话术内容、添加人" | ||||
|             clearable | ||||
|             @clear="search.current = 1, search.title = '', getList()" | ||||
|             suffix-icon="iconfont iconSearch"> | ||||
|           </el-input> | ||||
|         </template> | ||||
|       </ai-search-bar> | ||||
|       <ai-table | ||||
|         :tableData="tableData" | ||||
|         :col-configs="colConfigs" | ||||
|         :total="total" | ||||
|         style="margin-top: 6px; width: 100%;" | ||||
|         :current.sync="search.current" | ||||
|         :size.sync="search.size" | ||||
|         @getList="getList"> | ||||
|         <el-table-column slot="options" width="120px" fixed="right" label="操作" align="center"> | ||||
|           <template slot-scope="{ row }"> | ||||
|             <div class="table-options"> | ||||
|               <el-button type="text" @click="id = row.id, toAdd(row)">编辑</el-button> | ||||
|               <el-button type="text" @click="remove(row.id)">删除</el-button> | ||||
|             </div> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|       </ai-table> | ||||
|       <ai-dialog | ||||
|         :visible.sync="isShow" | ||||
|         width="920px" | ||||
|         :title="(id ? '编辑' : '添加') + typeList[currIndex]" | ||||
|         @close="onClose" | ||||
|         :close-on-click-modal="false" | ||||
|         destroy-on-close | ||||
|         @onConfirm="confirm"> | ||||
|         <el-form ref="form" v-if="isShow" :model="form" label-width="130px" label-position="right"> | ||||
|           <div class="ai-form"> | ||||
|             <el-form-item v-if="currIndex === 0" label="话术标题" style="width: 100%;" prop="title" :rules="[{ required: true, message: '请输入话术标题', trigger: 'blur' }]"> | ||||
|               <el-input | ||||
|                 size="small" | ||||
|                 placeholder="请输入话术标题" | ||||
|                 maxlength="42" | ||||
|                 show-word-limit | ||||
|                 v-model="form.title"> | ||||
|               </el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 0" label="话术内容" style="width: 100%;" prop="content" :rules="[{ required: true, message: '请输入话术内容', trigger: 'blur' }]"> | ||||
|               <el-input | ||||
|                 size="small" | ||||
|                 placeholder="请输入话术内容" | ||||
|                 maxlength="682" | ||||
|                 show-word-limit | ||||
|                 v-model="form.content"> | ||||
|               </el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 1" label="图片名称" style="width: 100%;" prop="title" :rules="[{ required: true, message: '请输入图片名称', trigger: 'blur' }]"> | ||||
|               <el-input | ||||
|                 size="small" | ||||
|                 placeholder="请输入图片名称" | ||||
|                 maxlength="42" | ||||
|                 show-word-limit | ||||
|                 v-model="form.title"> | ||||
|               </el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 1" label="上传图片" style="width: 100%;" prop="fileUrl" :rules="[{ required: true, message: '请上传', trigger: 'change' }]"> | ||||
|               <ai-uploader :instance="instance" url="/admin/file/add3?type=image" v-model="form.fileUrl" :limit="1"></ai-uploader> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 3" label="文件名称" style="width: 100%;" prop="title" :rules="[{ required: true, message: '请输入文件名称', trigger: 'blur' }]"> | ||||
|               <el-input | ||||
|                 size="small" | ||||
|                 placeholder="请输入图片名称" | ||||
|                 maxlength="42" | ||||
|                 show-word-limit | ||||
|                 v-model="form.title"> | ||||
|               </el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 3" label="上传文件" style="width: 100%;" prop="fileUrl" :rules="[{ required: true, message: '请上传', trigger: 'change' }]"> | ||||
|               <ai-uploader fileType="file" url="/admin/file/add3?type=file" :instance="instance" v-model="form.fileUrl" :limit="1"></ai-uploader> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 4" label="视频标题" style="width: 100%;" prop="title" :rules="[{ required: true, message: '请输入视频标题', trigger: 'blur' }]"> | ||||
|               <el-input | ||||
|                 size="small" | ||||
|                 placeholder="请输入视频标题" | ||||
|                 maxlength="42" | ||||
|                 show-word-limit | ||||
|                 v-model="form.title"> | ||||
|               </el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 4" label="上传视频" style="width: 100%;" prop="fileUrl" :rules="[{ required: true, message: '请上传', trigger: 'change' }]"> | ||||
|               <ai-uploader :instance="instance" url="/admin/file/add3?type=video" fileType="file" v-model="form.fileUrl" :limit="1"></ai-uploader> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 4" label="视频封面图" prop="pictureUrl" style="width: 100%;" :rules="[{ required: true, message: '请上传视频封面图', trigger: 'change' }]"> | ||||
|               <ai-uploader :instance="instance" url="/admin/file/add3?type=image" v-model="form.pictureUrl" :limit="1"></ai-uploader> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 4" label="视频描述" style="width: 100%;" prop="content" :rules="[{ required: true, message: '请输入视频描述', trigger: 'blur' }]"> | ||||
|               <el-input | ||||
|                 size="small" | ||||
|                 placeholder="请输入视频描述" | ||||
|                 maxlength="682" | ||||
|                 show-word-limit | ||||
|                 v-model="form.content"> | ||||
|               </el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 5" label="网页标题" style="width: 100%;" prop="title" :rules="[{ required: true, message: '请输入网页标题', trigger: 'blur' }]"> | ||||
|               <el-input | ||||
|                 size="small" | ||||
|                 placeholder="请输入网页标题" | ||||
|                 maxlength="42" | ||||
|                 show-word-limit | ||||
|                 v-model="form.title"> | ||||
|               </el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 5" label="网页链接" style="width: 100%;" prop="pagePath" :rules="[{ required: true, message: '请输入网页链接链接', trigger: 'blur' }]"> | ||||
|               <el-input | ||||
|                 size="small" | ||||
|                 placeholder="请输入网页链接链接" | ||||
|                 maxlength="682" | ||||
|                 show-word-limit | ||||
|                 v-model="form.pagePath"> | ||||
|               </el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="form.currIndex === 5" label="网页描述" style="width: 100%;" prop="content"> | ||||
|               <el-input | ||||
|                 size="small" | ||||
|                 placeholder="请输入网页描述" | ||||
|                 maxlength="170" | ||||
|                 show-word-limit | ||||
|                 v-model="form.content"> | ||||
|               </el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 5" label="网页封面图" prop="pictureUrl" style="width: 100%;" :rules="[{ required: true, message: '请上传封面图', trigger: 'change' }]"> | ||||
|               <ai-uploader :instance="instance" url="/admin/file/add3?type=image" v-model="form.pictureUrl" :limit="1"></ai-uploader> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 2" label="小程序标题" style="width: 100%;" prop="title" :rules="[{ required: true, message: '请输入小程序标题', trigger: 'blur' }]"> | ||||
|               <el-input | ||||
|                 size="small" | ||||
|                 placeholder="请输入小程序标题" | ||||
|                 maxlength="20" | ||||
|                 show-word-limit | ||||
|                 v-model="form.title"> | ||||
|               </el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 2" label="小程序appid" style="width: 100%;" prop="appId" :rules="[{ required: true, message: '小程序appid', trigger: 'blur' }]"> | ||||
|               <el-input | ||||
|                 size="small" | ||||
|                 placeholder="小程序appid" | ||||
|                 v-model="form.appId"> | ||||
|               </el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 2" label="小程序路径" style="width: 100%;" prop="pagePath" :rules="[{ required: true, message: '请输入小程序路径', trigger: 'blur' }]"> | ||||
|               <el-input | ||||
|                 size="small" | ||||
|                 placeholder="请输入小程序page路径" | ||||
|                 v-model="form.pagePath"> | ||||
|               </el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item v-if="currIndex === 2" label="小程序封面图" prop="pictureUrl" style="width: 100%;" :rules="[{ required: true, message: '请上传封面图', trigger: 'change' }]"> | ||||
|               <ai-uploader :instance="instance" url="/admin/file/add3?type=image" v-model="form.pictureUrl" :limit="1"></ai-uploader> | ||||
|             </el-form-item> | ||||
|           </div> | ||||
|         </el-form> | ||||
|       </ai-dialog> | ||||
|     </template> | ||||
|   </ai-list> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   export default { | ||||
|     name: 'List', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object | ||||
|     }, | ||||
|  | ||||
|     data() { | ||||
|       return { | ||||
|         search: { | ||||
|           current: 1, | ||||
|           size: 10, | ||||
|           title: '', | ||||
|         }, | ||||
|         isShow: false, | ||||
|         form: { | ||||
|           appId: '', | ||||
|           content: '', | ||||
|           fileUrl: [], | ||||
|           pagePath: '', | ||||
|           pictureUrl: [], | ||||
|           title: '' | ||||
|         }, | ||||
|         id: '', | ||||
|         typeList: ['话术', '图片', '小程序', '文件', '视频', '网页'], | ||||
|         currIndex: 0, | ||||
|         tableData: [], | ||||
|         total: 0 | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|       mpTitle () { | ||||
|         return { | ||||
|           '0': '话术标题', | ||||
|           '1': '图片名称', | ||||
|           '2': '小程序标题', | ||||
|           '3': '文件名称', | ||||
|           '4': '视频名称', | ||||
|           '5': '网页名称' | ||||
|         }[this.currIndex] | ||||
|       }, | ||||
|  | ||||
|       colConfigs () { | ||||
|         if (this.currIndex === 0) { | ||||
|           return [ | ||||
|             { prop: 'title', label: this.mpTitle }, | ||||
|             { prop: 'content', label: '话术内容', align: 'center' }, | ||||
|             { prop: 'createUserName', label: '添加人', align: 'center' }, | ||||
|             { prop: 'createTime', label: '添加时间', align: 'center' } | ||||
|           ] | ||||
|         } | ||||
|  | ||||
|         if (this.currIndex === 2) { | ||||
|           return [ | ||||
|             { prop: 'title', label: this.mpTitle }, | ||||
|             { prop: 'appId', label: '小程序APPID', align: 'center' }, | ||||
|             { prop: 'createUserName', label: '添加人', align: 'center' }, | ||||
|             { prop: 'createTime', label: '添加时间', align: 'center' } | ||||
|           ] | ||||
|         } | ||||
|  | ||||
|         if (this.currIndex === 5) { | ||||
|           return [ | ||||
|             { prop: 'title', label: this.mpTitle }, | ||||
|             { prop: 'pagePath', label: '外链网页', align: 'center' }, | ||||
|             { prop: 'createUserName', label: '添加人', align: 'center' }, | ||||
|             { prop: 'createTime', label: '添加时间', align: 'center' } | ||||
|           ] | ||||
|         } | ||||
|  | ||||
|         return [ | ||||
|           { prop: 'title', label: this.mpTitle }, | ||||
|           { prop: 'fileSizeStr', label: '文件大小', align: 'center' }, | ||||
|           { prop: 'createUserName', label: '添加人', align: 'center' }, | ||||
|           { prop: 'createTime', label: '添加时间', align: 'center' } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     created () { | ||||
|       this.getList() | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       getList() { | ||||
|         this.instance.post(`/app/appmaterialinfo/list`, null, { | ||||
|           params: { | ||||
|             ...this.search, | ||||
|             type: this.currIndex | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.tableData = res.data.records | ||||
|             this.total = res.data.total | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       toAdd (e) { | ||||
|         this.form = { | ||||
|           ...this.form, | ||||
|           ...e, | ||||
|           fileUrl: e.fileUrl ? [{ | ||||
|             url: e.fileUrl | ||||
|           }] : '', | ||||
|           pictureUrl: e.pictureUrl ? [{ | ||||
|             url: e.pictureUrl | ||||
|           }] : '' | ||||
|         } | ||||
|  | ||||
|         this.isShow = true | ||||
|       }, | ||||
|  | ||||
|       onClose () { | ||||
|         this.form.content = '' | ||||
|         this.form.appId = '' | ||||
|         this.form.fileUrl = [] | ||||
|         this.form.pagePath = '' | ||||
|         this.form.pictureUrl = [] | ||||
|         this.form.title = '' | ||||
|  | ||||
|         this.id = '' | ||||
|       }, | ||||
|  | ||||
|       confirm () { | ||||
|         this.$refs.form.validate((valid) => { | ||||
|           if (valid) { | ||||
|             this.instance.post(`/app/appmaterialinfo/addOrUpdate`, { | ||||
|               ...this.form, | ||||
|               type: this.currIndex, | ||||
|               fileUrl: this.form.fileUrl.length ? this.form.fileUrl[0].url : '', | ||||
|               fileId: this.form.fileUrl.length ? this.form.fileUrl[0].id : '', | ||||
|               pictureId: this.form.pictureUrl.length ? this.form.pictureUrl[0].id : '', | ||||
|               pictureUrl: this.form.pictureUrl.length ? this.form.pictureUrl[0].url : '', | ||||
|               fileSize: this.form.fileUrl.length ? this.form.fileUrl[0].fileSize : '', | ||||
|             }).then(res => { | ||||
|               if (res.code == 0) { | ||||
|                 this.$message.success('提交成功') | ||||
|                 this.isShow = false | ||||
|  | ||||
|                 this.getList() | ||||
|               } | ||||
|             }) | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       remove (id) { | ||||
|         this.$confirm('确定删除该数据?').then(() => { | ||||
|           this.instance.post(`/app/appmaterialinfo/delete?ids=${id}`).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               this.$message.success('删除成功!') | ||||
|               this.getList() | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   #AppMaterialLibrary { | ||||
|     :deep( .ai-list__content--right-wrapper ) { | ||||
|       padding: 0 20px!important; | ||||
|     } | ||||
|  | ||||
|     .AppMaterialLibrary-title { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       margin-bottom: 20px; | ||||
|       border-bottom: 1px solid #eee; | ||||
|  | ||||
|       span { | ||||
|         height: 100%; | ||||
|         line-height: 56px; | ||||
|         margin-right: 32px; | ||||
|         color: #888888; | ||||
|         font-size: 16px; | ||||
|         font-weight: 600; | ||||
|         transition: all ease 0.3s; | ||||
|         border-bottom: 3px solid transparent; | ||||
|         cursor: pointer; | ||||
|         user-select: none; | ||||
|  | ||||
|         &:hover { | ||||
|           color: #222; | ||||
|         } | ||||
|  | ||||
|         &:last-child { | ||||
|           margin-right: 0; | ||||
|         } | ||||
|  | ||||
|         &.active { | ||||
|           color: #222222; | ||||
|           border-bottom: 3px solid #2266FF; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
| @@ -1,617 +0,0 @@ | ||||
| <template> | ||||
|   <section style="height: 100%;"> | ||||
|     <ai-list> | ||||
|       <template slot="title"> | ||||
|         <ai-title title="话术库" :isShowBottomBorder="true"></ai-title> | ||||
|       </template> | ||||
|       <template #left> | ||||
|         <div class="left-tree"> | ||||
|           <div class="tree-title"> | ||||
|             <label>分组</label> | ||||
|             <el-button icon="iconfont iconAdd" style="height: 28px;line-height: 0;" @click="groupDialog = true">添加分组 | ||||
|             </el-button> | ||||
|           </div> | ||||
|           <el-scrollbar style="height: calc(100% - 40px)"> | ||||
|             <el-menu style="height: 100%;" default-active="0" active-text-color="#26f" text-color="#222" | ||||
|                      mode="vertical"> | ||||
|               <el-menu-item v-for="(item, index) in sortList" :key="index" class="menu-item" @click="currentMenu(item)" | ||||
|                             :index="index.toString()"> | ||||
|                 <label class="item-title">{{ item.name }}</label> | ||||
|                 <el-popover style="width: 30px" placement="left" trigger="click"> | ||||
|                   <div style="display: flex;flex-direction: column;align-items: center;"> | ||||
|                     <div style="cursor: pointer;" @click="editGroup(item, index)">编辑</div> | ||||
|                     <div style="cursor: pointer;" @click="delteGroup(item, index)">删除</div> | ||||
|                   </div> | ||||
|                   <span class="iconfont iconMore" slot="reference"></span> | ||||
|                 </el-popover> | ||||
|               </el-menu-item> | ||||
|             </el-menu> | ||||
|           </el-scrollbar> | ||||
|         </div> | ||||
|       </template> | ||||
|       <template slot="content"> | ||||
|         <ai-search-bar> | ||||
|           <template slot="left"> | ||||
|             <el-select v-model="search.contentType" placeholder="类型" size="small" clearable @change="getList()"> | ||||
|               <el-option v-for="(item, index) in types" :key="index" :label="item.name" :value="item.value"></el-option> | ||||
|             </el-select> | ||||
|           </template> | ||||
|           <template slot="right"> | ||||
|             <el-input v-model="search.title" class="search-input" size="small" | ||||
|                       @keyup.enter.native=";(search.current = 1), getList()" placeholder="请输入标题或话术内容或创建人" clearable | ||||
|                       @clear=";(search.current = 1), (search.title = ''), getList()" suffix-icon="iconfont iconSearch"/> | ||||
|           </template> | ||||
|         </ai-search-bar> | ||||
|         <ai-search-bar> | ||||
|           <template slot="left"> | ||||
|             <el-button icon="iconfont iconAdd" type="primary" @click="add">添加话术</el-button> | ||||
|           </template> | ||||
|         </ai-search-bar> | ||||
|         <ai-table :tableData="tableData" :col-configs="colConfigs" :stripe="true" :total="total" ref="aitableex" | ||||
|                   style="margin-top: 8px;" :current.sync="search.current" :size.sync="search.size" @getList="getList">\ | ||||
|  | ||||
|           <el-table-column slot="content" label="话术内容" align="left" width="280"> | ||||
|             <template slot-scope="{ row }"> | ||||
|               <div class="table-left__wrapper"> | ||||
|                 <video :src="row.file && row.file.url" v-if="row.file && row.contentType == 'video'" | ||||
|                        class="media"></video> | ||||
|                 <img :src="row.file && row.file.url" v-if="row.file && row.contentType == 'image'" class="media"/> | ||||
|                 <audio :src="row.file && row.file.url" v-if="row.file && row.contentType == 'voice'" | ||||
|                        class="media"></audio> | ||||
|                 <div class="table-left__wrapper--right"> | ||||
|                   <el-tooltip class="item" effect="dark" :content="row.content" placement="top"> | ||||
|                     <div class="table-left__wrapper--text" v-if="row.contentType == 'text'">{{ row.content }}</div> | ||||
|                   </el-tooltip> | ||||
|                   <div class="ellipsis" v-if="row.contentType !== 'text' && row.file && row.file.name">{{ row.file.name }}</div> | ||||
|                   <div v-if="row.contentType !== 'text' && row.file && row.file.size">{{ row.file.size | size }}KB</div> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </template> | ||||
|           </el-table-column> | ||||
|  | ||||
|  | ||||
|           <el-table-column slot="options" label="操作" width="160px" align="center" fixed="right"> | ||||
|             <template slot-scope="{ row }"> | ||||
|               <span class="table-btn" title="编辑" @click="editTrick(row)">编辑</span> | ||||
|               <span class="table-btn" title="删除" @click="deleteTrick(row)">删除</span> | ||||
|             </template> | ||||
|           </el-table-column> | ||||
|         </ai-table> | ||||
|       </template> | ||||
|     </ai-list> | ||||
|     <ai-dialog :visible.sync="groupDialog" @closed="sortForm = {},sortForm.type=1" width="800px" title="添加分组" | ||||
|                @onConfirm="onConfirm"> | ||||
|       <el-form size="small" label-width="100px" ref="groupForm" :model="sortForm" :rules="sortRules"> | ||||
|         <el-form-item label="分组名称" prop="name"> | ||||
|           <el-input clearable v-model.trim="sortForm.name" placeholder="请输入..." :maxlength="10" show-word-limit/> | ||||
|         </el-form-item> | ||||
|         <!--        <el-form-item label="可见范围" prop="type">--> | ||||
|         <!--          <el-radio-group v-model="sortForm.type">--> | ||||
|         <!--            <el-radio label="0">个人话术</el-radio>--> | ||||
|         <!--            <el-radio label="1">公共话术</el-radio>--> | ||||
|         <!--          </el-radio-group>--> | ||||
|         <!--        </el-form-item>--> | ||||
|         <!--        <el-form-item label="可用部门" prop="documentName">--> | ||||
|         <!--          <el-row type="flex">--> | ||||
|         <!--            <div class="input"></div>--> | ||||
|         <!--            <ai-person-select :instance="instance" url="/app/appvillagecadres/list" :isMultiple="true"--> | ||||
|         <!--                              btnText="选择" dialogTitle="选择">--> | ||||
|         <!--              <template name="option" v-slot:option="{ item }">--> | ||||
|         <!--                <span class="iconfont iconProlife">{{ item.name }}</span>--> | ||||
|         <!--                <ai-id mode="show" :show-eyes="false" :value="item.idNumber"/>--> | ||||
|         <!--              </template>--> | ||||
|         <!--            </ai-person-select>--> | ||||
|         <!--          </el-row>--> | ||||
|         <!--        </el-form-item>--> | ||||
|       </el-form> | ||||
|     </ai-dialog> | ||||
|  | ||||
|     <ai-dialog :visible.sync="trickDialog" @close="onClose" @onConfirm="trickConfirm" width="800px" title="添加话术"> | ||||
|       <el-form size="small" label-width="100px" ref="trickFom" :model="trickForm" :rules="trickRules"> | ||||
|         <!--        <el-form-item label="分组" prop="handleResult">--> | ||||
|         <!--          <ai-select--> | ||||
|         <!--            v-model="search.type"--> | ||||
|         <!--            @change="search.current = 1, getList()"--> | ||||
|         <!--            placeholder="类型"--> | ||||
|         <!--            :selectList="sortList"--> | ||||
|         <!--          ></ai-select>--> | ||||
|         <!--        </el-form-item>--> | ||||
|         <el-form-item label="标题" prop="title"> | ||||
|           <el-input clearable placeholder="请输入..." v-model="trickForm.title" show-word-limit :maxlength="20"/> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="话术类型" prop="contentType"> | ||||
|           <el-radio-group v-model="trickForm.contentType" @change="onChange"> | ||||
|             <el-radio :label="item.value" v-for="(item, index) in types" :key="index">{{ item.name }}</el-radio> | ||||
|           </el-radio-group> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="文本内容" class="el-form-item__textarea" prop="content" v-if="trickForm.contentType == 'text'"> | ||||
|           <span @click="insertNickname" class="el-form-item__btn" type="text">[插入用户昵称]</span> | ||||
|           <span @click="insertRemark" class="el-form-item__remark" type="text">[插入备注名]</span> | ||||
|           <el-input type="textarea" v-model="trickForm.content" placeholder="请输入..." :rows="5" show-word-limit | ||||
|                     :maxlength="255"/> | ||||
|         </el-form-item> | ||||
|         <el-form-item :label="compLabel" prop="fileId" v-else> | ||||
|           <ai-uploader :instance="instance" v-model="fileList" :acceptType="acceptType" | ||||
|                        :url="'/app/wxcp/upload/uploadFile?type=' + trickForm.contentType" isWechat :fileType="fileType" | ||||
|                        :limit="1" @change="change"></ai-uploader> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|     </ai-dialog> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapState} from 'vuex' | ||||
|  | ||||
| export default { | ||||
|   name: 'AppVerbalTrick', | ||||
|   label: '话术库', | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       groupDialog: false, | ||||
|       trickDialog: false, | ||||
|       search: { | ||||
|         current: 1, | ||||
|         size: 10, | ||||
|         contentType: '', | ||||
|       }, | ||||
|       sortForm: { | ||||
|         name: '', | ||||
|         type: '1', | ||||
|       }, | ||||
|       trickForm: { | ||||
|         title: '', | ||||
|         contentType: 'text', | ||||
|         content: '', | ||||
|         fileId: '', | ||||
|         mediaId: '', | ||||
|       }, | ||||
|       row: {}, | ||||
|       current: {}, | ||||
|       sortList: [], | ||||
|       total: 0, | ||||
|       tableData: [], | ||||
|       fileList: [], | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|     acceptType() { | ||||
|       return { | ||||
|         "image": ".jpg,.png,.jpeg", | ||||
|         "video": ".mp4" | ||||
|       }[this.trickForm.contentType] | ||||
|     }, | ||||
|     compLabel() { | ||||
|       return this.types.find((e) => e.value == this.trickForm.contentType)?.name | ||||
|     }, | ||||
|     fileType() { | ||||
|       return this.trickForm.contentType == 'image' ? 'img' : 'file' | ||||
|     }, | ||||
|     types() { | ||||
|       return [ | ||||
|         {name: '文本', value: 'text'}, | ||||
|         {name: '图片', value: 'image'}, | ||||
|         {name: '视频', value: 'video'}, | ||||
|         // // {name: "附件", value: "file"}, | ||||
|         // {name: "音频", value: "voice"}, | ||||
|       ] | ||||
|     }, | ||||
|     trickRules() { | ||||
|       return { | ||||
|         title: [{required: true, message: '请输入标题', trigger: 'blur'}], | ||||
|         contentType: [{required: true, message: '请选择话术类型', trigger: 'change'}], | ||||
|         content: [{required: true, validator: (rule, value, callback)=>{ | ||||
|             if(this.trickForm.contentType == 'text' && !value){ | ||||
|               return callback("请输入文本内容") | ||||
|             }else { | ||||
|               callback() | ||||
|             } | ||||
|           }}], | ||||
|         fileId: [{required: true, validator: (rule, value, callback)=>{ | ||||
|             if(this.trickForm.contentType != 'text' && !value){ | ||||
|               return callback("请上传文件") | ||||
|             }else { | ||||
|               callback() | ||||
|             } | ||||
|           }}], | ||||
|       } | ||||
|     }, | ||||
|     // message() { | ||||
|     //   return (() => { | ||||
|     //     if (this.trickForm.contentType == 'text') { | ||||
|     //       return '请输入文本内容' | ||||
|     //     } else if (this.trickForm.contentType == 'image') { | ||||
|     //       return '请上传图片' | ||||
|     //     } else if (this.trickForm.contentType == 'file') { | ||||
|     //       return '请上传文件' | ||||
|     //     } else if (this.trickForm.contentType == 'video') { | ||||
|     //       return '请上传视频' | ||||
|     //     } else if (this.trickForm.contentType == 'voice') { | ||||
|     //       return '请上传音频' | ||||
|     //     } | ||||
|     //   })() | ||||
|     // }, | ||||
|     sortRules() { | ||||
|       return { | ||||
|         name: [{required: true, message: '请输入分组名称', trigger: 'blur'}], | ||||
|         type: [{required: true, message: '请选择可见范围', trigger: 'change'}], | ||||
|       } | ||||
|     }, | ||||
|     colConfigs() { | ||||
|       return [ | ||||
|         {slot: 'type'}, | ||||
|         {slot: 'content'}, | ||||
|         {prop: 'title', label: '标题'}, | ||||
|         { | ||||
|           prop: 'contentType', | ||||
|           label: '类型', | ||||
|           align: 'center', | ||||
|           render: (h, {row}) => { | ||||
|             return h('span', {}, this.types.find((e) => row.contentType == e.value)?.name) | ||||
|           }, | ||||
|         }, | ||||
|         {prop: 'userName', label: '创建人', align: 'center'}, | ||||
|         {prop: 'createTime', label: '创建时间', align: 'center'}, | ||||
|         {slot: 'options', label: '操作', align: 'center'}, | ||||
|       ] | ||||
|     }, | ||||
|   }, | ||||
|  | ||||
|   created() { | ||||
|     this.getSortList() | ||||
|   }, | ||||
|  | ||||
|   filters: { | ||||
|     size(size) { | ||||
|       return (size / 1024).toFixed(1) | ||||
|     }, | ||||
|   }, | ||||
|  | ||||
|   methods: { | ||||
|     insertNickname() { | ||||
|       this.trickForm.content += '[用户昵称]' | ||||
|     }, | ||||
|     insertRemark() { | ||||
|       this.trickForm.content += '[备注名]' | ||||
|     }, | ||||
|     onClose() { | ||||
|       this.trickForm.title = '' | ||||
|       this.trickForm.content = '' | ||||
|       this.trickForm.fileId = '' | ||||
|       this.trickForm.mediaId = '' | ||||
|       this.trickForm.createdAt = '' | ||||
|       this.trickForm.contentType = 'text' | ||||
|       this.fileList = [] | ||||
|     }, | ||||
|     onChange() { | ||||
|       this.trickForm.fileId = '' | ||||
|       this.trickForm.content = '' | ||||
|       this.trickForm.mediaId = '' | ||||
|       this.trickForm.createdAt = '' | ||||
|       this.fileList = [] | ||||
|     }, | ||||
|     add() { | ||||
|       if (this.sortList.length == 0) return this.$message.error('请先添加分组') | ||||
|       this.$nextTick(() => { | ||||
|         this.trickDialog = true | ||||
|       }) | ||||
|     }, | ||||
|     change(e) { | ||||
|       if(e.length){ | ||||
|         this.trickForm.fileId = e[0].id | ||||
|         this.trickForm.mediaId = e[0].media.mediaId | ||||
|         this.trickForm.createdAt = e[0].media.createdAt | ||||
|       }else { | ||||
|         this.trickForm.fileId ="" | ||||
|         this.trickForm.mediaId = "" | ||||
|         this.trickForm.createdAt = "" | ||||
|       } | ||||
|     }, | ||||
|     editTrick(row) { | ||||
|       this.row = row | ||||
|       this.trickForm = {...row} | ||||
|       row.file && (this.trickForm.fileId = row.file.id) | ||||
|       this.$nextTick(() => { | ||||
|         this.trickDialog = true | ||||
|       }) | ||||
|       this.fileList.push(row.file) | ||||
|     }, | ||||
|     deleteTrick(row) { | ||||
|       this.$confirm('确认要删除吗?').then(() => { | ||||
|         this.instance | ||||
|         .post(`/app/wxcp/wxspeechtechnique/delete`, null, { | ||||
|           params: { | ||||
|             id: row.id, | ||||
|           }, | ||||
|         }) | ||||
|         .then((res) => { | ||||
|           if (res.code == 0) { | ||||
|             this.$message.success('删除成功') | ||||
|             this.getList() | ||||
|           } | ||||
|         }) | ||||
|       }) | ||||
|     }, | ||||
|     trickConfirm() { | ||||
|       this.$refs['trickFom'].validate((valid) => { | ||||
|         if (valid) { | ||||
|           this.instance | ||||
|           .post(`/app/wxcp/wxspeechtechnique/addOrUpdate`, { | ||||
|             ...this.trickForm, | ||||
|             category: this.current.id, | ||||
|             userId: this.user.info.id, | ||||
|           }) | ||||
|           .then((res) => { | ||||
|             if (res.code == 0) { | ||||
|               this.trickDialog = false | ||||
|               this.$message.success(this.trickForm.id ? '编辑成功' : '添加成功') | ||||
|               this.getList() | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     currentMenu(item) { | ||||
|       this.current = item | ||||
|       this.getList() | ||||
|     }, | ||||
|     delteGroup(item, index) { | ||||
|       this.$confirm('是否要删除?').then(() => { | ||||
|         this.instance | ||||
|         .post(`/app/wxcp/wxspeechtechniquecategory/delete`, null, { | ||||
|           params: { | ||||
|             id: item.id, | ||||
|           }, | ||||
|         }) | ||||
|         .then((res) => { | ||||
|           if (res.code == 0) { | ||||
|             this.$message.success('删除成功') | ||||
|             this.getSortList() | ||||
|           } | ||||
|         }) | ||||
|       }) | ||||
|     }, | ||||
|     editGroup(item, index) { | ||||
|       this.sortForm = {...item} | ||||
|       this.groupDialog = true | ||||
|     }, | ||||
|     onConfirm() { | ||||
|       this.$refs['groupForm'].validate((valid) => { | ||||
|         if (valid) { | ||||
|           this.instance | ||||
|           .post(`/app/wxcp/wxspeechtechniquecategory/addOrUpdate`, { | ||||
|             ...this.sortForm, | ||||
|           }) | ||||
|           .then((res) => { | ||||
|             if (res.code == 0) { | ||||
|               this.groupDialog = false | ||||
|               this.$message.success(this.sortForm.id ? '编辑成功' : '添加成功') | ||||
|               this.getSortList() | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     getSortList() { | ||||
|       this.instance | ||||
|       .post(`/app/wxcp/wxspeechtechniquecategory/listAll`, null, { | ||||
|         params: { | ||||
|           // type: 1, | ||||
|         }, | ||||
|       }) | ||||
|       .then((res) => { | ||||
|         if (res && res.data.length) { | ||||
|           this.sortList = res.data | ||||
|           this.current = res.data[0] | ||||
|           this.getList() | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     getList(item) { | ||||
|       this.instance | ||||
|       .post(`/app/wxcp/wxspeechtechnique/list`, null, { | ||||
|         params: { | ||||
|           category: this.current.id, | ||||
|           ...this.search, | ||||
|           status: 1, | ||||
|         }, | ||||
|       }) | ||||
|       .then((res) => { | ||||
|         if (res.code == 0) { | ||||
|           this.tableData = res.data.records | ||||
|           this.total = res.data.total | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .left-tree { | ||||
|   width: 264px; | ||||
|   background: #fafafb; | ||||
|   box-shadow: -1px 0px 0px 0px #e5e5e5; | ||||
|   border-radius: 2px 0px 0px 2px; | ||||
|  | ||||
|   .tree-title { | ||||
|     height: 40px; | ||||
|     box-sizing: border-box; | ||||
|     padding: 0 8px 0 16px; | ||||
|     background: #eeeff1; | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     justify-content: space-between; | ||||
|  | ||||
|     & > label { | ||||
|       font-size: 14px; | ||||
|       font-weight: bold; | ||||
|       color: #222222; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .tree-item { | ||||
|     box-sizing: border-box; | ||||
|     padding: 8px 16px 8px 24px; | ||||
|     height: 40px; | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     justify-content: space-between; | ||||
|     border-right: 2px solid transparent; | ||||
|   } | ||||
|  | ||||
|   .menu-item { | ||||
|     height: 40px; | ||||
|     align-items: center; | ||||
|     display: flex; | ||||
|     justify-content: space-between; | ||||
|  | ||||
|     .item-title { | ||||
|       display: inline-block; | ||||
|       width: 160px; | ||||
|       overflow: hidden; | ||||
|       text-overflow: ellipsis; | ||||
|       white-space: nowrap; | ||||
|       font-size: 14px; | ||||
|       color: #222222; | ||||
|     } | ||||
|  | ||||
|     .iconMore { | ||||
|       cursor: pointer; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   :deep( .el-scrollbar__wrap ){ | ||||
|     overflow-x: hidden; | ||||
|   } | ||||
| } | ||||
|  | ||||
| :deep( .ai-list__content--right ){ | ||||
|  | ||||
|   .ai-list__content--right-wrapper { | ||||
|     min-height: 100%; | ||||
|   } | ||||
| } | ||||
|  | ||||
| :deep( .has-gutter tr ){ | ||||
|   height: 40px; | ||||
| } | ||||
|  | ||||
| :deep( .el-table__row td ){ | ||||
|   height: 68px; | ||||
| } | ||||
|  | ||||
| // :deep( .el-table__row td .cell ){ | ||||
| //   width: 240px; | ||||
| //   height: 44px; | ||||
| // } | ||||
|  | ||||
| .table-btn { | ||||
|   font-size: 14px; | ||||
|   color: #2266ff; | ||||
|   cursor: pointer; | ||||
|  | ||||
|   &:nth-child(1) { | ||||
|     margin-right: 16px; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .input { | ||||
|   width: 100%; | ||||
|   min-height: 32px; | ||||
|   line-height: 32px; | ||||
|   border-radius: 4px; | ||||
|   border: 1px solid #d0d4dc; | ||||
|   color: #666; | ||||
|   display: inline-block; | ||||
|   font-size: inherit; | ||||
|   cursor: pointer; | ||||
|  | ||||
|   &:after { | ||||
|     content: '请选择...'; | ||||
|     color: #888888; | ||||
|     padding-left: 10px; | ||||
|   } | ||||
| } | ||||
|  | ||||
| :deep( .media ){ | ||||
|   object-fit: fill; | ||||
|   width: 40px; | ||||
|   height: 40px; | ||||
|   vertical-align: middle; | ||||
|   box-sizing: content-box; | ||||
| } | ||||
|  | ||||
| :deep( .AiPersonSelect ){ | ||||
|   & > button { | ||||
|     background: #f5f5f5; | ||||
|     border-radius: 0px 2px 2px 0px; | ||||
|     border: 1px solid #d0d4dc; | ||||
|     color: #222222; | ||||
|   } | ||||
| } | ||||
|  | ||||
| :deep( .is-active ){ | ||||
|   border-right: 2px solid #2266ff; | ||||
| } | ||||
| </style> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| .el-popper { | ||||
|   min-width: 76px !important; | ||||
| } | ||||
|  | ||||
| .table-left__wrapper { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|  | ||||
|   .table-left__wrapper--text { | ||||
|     display: -webkit-box; | ||||
|     -webkit-box-orient: vertical; | ||||
|     -webkit-line-clamp: 2; | ||||
|     overflow: hidden; | ||||
|   } | ||||
|  | ||||
|   .ellipsis { | ||||
|     width: 170px; | ||||
|     height: 24px; | ||||
|     overflow: hidden; | ||||
|     text-overflow: ellipsis; | ||||
|     white-space: nowrap; | ||||
|   } | ||||
|  | ||||
|   img, | ||||
|   video { | ||||
|     margin-right: 8px; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .el-form-item__textarea { | ||||
|   position: relative; | ||||
|  | ||||
|   .el-form-item__btn, .el-form-item__remark { | ||||
|     position: absolute; | ||||
|     bottom: 12px; | ||||
|     left: 12px; | ||||
|     line-height: 1; | ||||
|     z-index: 1; | ||||
|     color: #2266FF; | ||||
|     font-size: 14px; | ||||
|     user-select: none; | ||||
|     background: #fff; | ||||
|     cursor: pointer; | ||||
|   } | ||||
|  | ||||
|   .el-form-item__remark { | ||||
|     left: 108px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -1,66 +0,0 @@ | ||||
| <template> | ||||
|   <div class="doc-circulation ailist-wrapper"> | ||||
|     <keep-alive :include="['List']"> | ||||
|       <component ref="component" :is="component" @change="onChange" :params="params" :instance="instance" :dict="dict"></component> | ||||
|     </keep-alive> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import List from './components/List' | ||||
|   import Add from './components/Add' | ||||
|  | ||||
|   export default { | ||||
|     name: 'AppVillageCode', | ||||
|     label: '一村一码', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       return { | ||||
|         component: 'List', | ||||
|         params: {}, | ||||
|         include: [] | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     components: { | ||||
|       Add, | ||||
|       List | ||||
|     }, | ||||
|  | ||||
|     mounted () { | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       onChange (data) { | ||||
|         if (data.type === 'Add') { | ||||
|           this.component = 'Add' | ||||
|           this.params = data.params | ||||
|         } | ||||
|  | ||||
|         if (data.type === 'list') { | ||||
|           this.component = 'List' | ||||
|           this.params = data.params | ||||
|  | ||||
|           this.$nextTick(() => { | ||||
|             if (data.isRefresh) { | ||||
|               this.$refs.component.getList() | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss"> | ||||
|   .doc-circulation { | ||||
|     height: 100%; | ||||
|     background: #F3F6F9; | ||||
|     overflow: auto; | ||||
|   } | ||||
| </style> | ||||
| @@ -1,119 +0,0 @@ | ||||
| <template> | ||||
|   <ai-detail> | ||||
|     <template slot="title"> | ||||
|       <ai-title title="添加二维码" isShowBack isShowBottomBorder @onBackClick="cancel(false)"> | ||||
|       </ai-title> | ||||
|     </template> | ||||
|     <template slot="content"> | ||||
|       <ai-card title="基本信息"> | ||||
|         <template #content> | ||||
|           <el-form class="ai-form" ref="form" :model="form" label-width="110px" label-position="right"> | ||||
|             <el-form-item label="地区" style="width: 100%;" prop="codeName"> | ||||
|               <span style="color: #666;">{{ form.areaName }}</span> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="二维码名称" prop="codeName" :rules="[{ required: true, message: '请输入二维码名称', trigger: 'blur' }]"> | ||||
|               <el-input size="small" maxlength="30" show-word-limit placeholder="请输入二维码名称" style="width: 328px;" v-model="form.codeName"></el-input> | ||||
|             </el-form-item> | ||||
|             <el-form-item style="width: 100%;" label="二维码类型" prop="type" :rules="[{ required: true, message: '请选择二维码类型', trigger: 'change' }]"> | ||||
|               <el-radio-group v-model="form.type"> | ||||
|                 <el-radio label="0">群二维码</el-radio> | ||||
|                 <el-radio label="1">个人二维码</el-radio> | ||||
|               </el-radio-group> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="上传二维码" prop="codeUrl" style="width: 100%;" :rules="[{ required: true, message: '请上传二维码', trigger: 'change' }]"> | ||||
|               <ai-uploader :instance="instance" v-model="form.codeUrl" :limit="1"></ai-uploader> | ||||
|             </el-form-item> | ||||
|           </el-form> | ||||
|         </template> | ||||
|       </ai-card> | ||||
|     </template> | ||||
|     <template #footer> | ||||
|       <el-button @click="cancel">取消</el-button> | ||||
|       <el-button type="primary" @click="confirm">提交</el-button> | ||||
|     </template> | ||||
|   </ai-detail> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   export default { | ||||
|     name: 'Add', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object, | ||||
|       params: Object | ||||
|     }, | ||||
|  | ||||
|     data () { | ||||
|       return { | ||||
|         info: {}, | ||||
|         form: { | ||||
|           areaId: '', | ||||
|           codeName: '', | ||||
|           areaName: '', | ||||
|           code: '', | ||||
|           codeUrl: [], | ||||
|           type: '', | ||||
|         }, | ||||
|         id: '' | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     created () { | ||||
|       if (this.params && this.params.areaId && !this.params.id) { | ||||
|         this.form.areaId = this.params.areaId | ||||
|         this.form.areaName = this.params.areaName | ||||
|       } | ||||
|  | ||||
|       if (this.params && this.params.id) { | ||||
|         this.id = this.params.id | ||||
|         this.getInfo(this.params.id) | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       getInfo (id) { | ||||
|         this.instance.post(`/app/appeveryvillagecode/queryDetailById?id=${id}`).then(res => { | ||||
|           if (res.code === 0) { | ||||
|             this.form = res.data | ||||
|             this.form.codeUrl = [{ | ||||
|               url: res.data.codeUrl | ||||
|             }] | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       onClose () { | ||||
|         this.form.explain = '' | ||||
|       }, | ||||
|  | ||||
|       confirm () { | ||||
|         this.$refs.form.validate((valid) => { | ||||
|           if (valid) { | ||||
|             this.instance.post(`/app/appeveryvillagecode/addOrUpdate`, { | ||||
|               ...this.form, | ||||
|               codeUrl: this.form.codeUrl[0].url | ||||
|             }).then(res => { | ||||
|               if (res.code == 0) { | ||||
|                 this.$message.success('提交成功') | ||||
|                 setTimeout(() => { | ||||
|                   this.cancel(true) | ||||
|                 }, 600) | ||||
|               } | ||||
|             }) | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       cancel (isRefresh) { | ||||
|         this.$emit('change', { | ||||
|           type: 'list', | ||||
|           isRefresh: !!isRefresh | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| </style> | ||||
| @@ -1,413 +0,0 @@ | ||||
| <template> | ||||
|   <ai-list class="villagecode"> | ||||
|     <template slot="title"> | ||||
|       <ai-title title="一村一码" isShowBottomBorder></ai-title> | ||||
|     </template> | ||||
|     <template #left> | ||||
|       <div class="villagecode-left"> | ||||
|         <div class="villagecode-left__title"> | ||||
|           <h2>村列表</h2> | ||||
|         </div> | ||||
|         <div class="addressBook-left__list"> | ||||
|           <div class="addressBook-left__list--title"> | ||||
|             <el-input | ||||
|               class="addressBook-left__list--search" | ||||
|               size="mini" | ||||
|               clearable | ||||
|               placeholder="请输入地区名称" | ||||
|               v-model="unitName" | ||||
|               suffix-icon="iconfont iconSearch"> | ||||
|             </el-input> | ||||
|           </div> | ||||
|           <el-tree | ||||
|             :filter-node-method="filterNode" | ||||
|             ref="tree" | ||||
|             :props="defaultProps" | ||||
|             node-key="id" | ||||
|             :data="areaTree" | ||||
|             highlight-current | ||||
|             :current-node-key="search.areaId" | ||||
|             :default-expanded-keys="defaultExpanded" | ||||
|             :default-checked-keys="defaultChecked" | ||||
|             @current-change="onTreeChange"> | ||||
|           </el-tree> | ||||
|         </div> | ||||
|       </div> | ||||
|     </template> | ||||
|     <template slot="content"> | ||||
|       <ai-search-bar class="search-bar"> | ||||
|         <template #left> | ||||
|           <el-button size="small" type="primary" :disabled="isShowAdd" icon="iconfont iconAdd" @click="toAdd('')">添加活码</el-button> | ||||
|         </template> | ||||
|       </ai-search-bar> | ||||
|       <ai-table | ||||
|         :tableData="tableData" | ||||
|         :col-configs="colConfigs" | ||||
|         :total="total" | ||||
|         style="margin-top: 6px;" | ||||
|         :current.sync="search.current" | ||||
|         :size.sync="search.size" | ||||
|         @getList="getList"> | ||||
|         <el-table-column slot="tags" label="标签"> | ||||
|           <template slot-scope="{ row }"> | ||||
|             <div class="table-tags"> | ||||
|               <el-tag type="info" v-for="(item, index) in row.tags" size="small" :key="index">{{ item }}</el-tag> | ||||
|             </div> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column slot="options" width="180px" fixed="right" label="操作" align="center"> | ||||
|           <template slot-scope="{ row }"> | ||||
|             <div class="table-options"> | ||||
|               <el-popover | ||||
|                   placement="bottom" | ||||
|                   width="160" | ||||
|                   :visible-arrow="false" | ||||
|                   popper-class="wechat-message__container" | ||||
|                   trigger="hover"> | ||||
|                 <el-button type="text" slot="reference">二维码</el-button> | ||||
|                 <div style="font-size: 0;"> | ||||
|                   <img class="message-info__img" :src="row.codeUrl"> | ||||
|                 </div> | ||||
|               </el-popover> | ||||
|               <el-button type="text" @click="toAdd(row.id)">编辑</el-button> | ||||
|               <el-button type="text" @click="remove(row.id)">删除</el-button> | ||||
|             </div> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|       </ai-table> | ||||
|     </template> | ||||
|   </ai-list> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import { mapState } from 'vuex' | ||||
|   export default { | ||||
|     name: 'List', | ||||
|  | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object | ||||
|     }, | ||||
|  | ||||
|     data() { | ||||
|       return { | ||||
|         search: { | ||||
|           current: 1, | ||||
|           size: 10, | ||||
|           status: 0, | ||||
|           title: '', | ||||
|           areaId: '' | ||||
|         }, | ||||
|         defaultExpanded: [], | ||||
|         defaultChecked: [], | ||||
|         areaTree: [], | ||||
|         defaultProps: { | ||||
|           children: 'children', | ||||
|           label: 'name' | ||||
|         }, | ||||
|         currIndex: -1, | ||||
|         total: 10, | ||||
|         colConfigs: [ | ||||
|           {prop: 'codeName', label: '名称', align: 'left'}, | ||||
|           {prop: 'type', label: '二维码类型', align: 'left', format: v => v === '0' ? '群二维码' : '个人二维码'}, | ||||
|           {prop: 'createUserName', label: '创建人'}, | ||||
|           {prop: 'createTime', label: '创建时间'}, | ||||
|           {slot: 'options', label: '操作'} | ||||
|         ], | ||||
|         areaName: '', | ||||
|         unitName: '', | ||||
|         tableData: [] | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|       ...mapState(['user']), | ||||
|  | ||||
|       isShowAdd () { | ||||
|         const str = this.search.areaId.substr(this.search.areaId.length - 3) | ||||
|  | ||||
|         return str === '000' | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     watch: { | ||||
|       unitName (val) { | ||||
|         this.$refs.tree.filter(val) | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     mounted() { | ||||
|       this.search.areaId = this.user.info.areaId | ||||
|       this.areaName = this.user.info.areaName | ||||
|       this.getTree() | ||||
|       this.getList() | ||||
|  | ||||
|       this.$nextTick(() => { | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|       getList() { | ||||
|         this.instance.post(`/app/appeveryvillagecode/list`, null, { | ||||
|           params: { | ||||
|             ...this.search | ||||
|           } | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.tableData = res.data.records | ||||
|             this.total = res.data.total | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       filterNode(value, data) { | ||||
|         if (!value) return true | ||||
|         return data.name.indexOf(value) !== -1 | ||||
|       }, | ||||
|  | ||||
|       onTreeChange (e) { | ||||
|         this.search.areaId = e.id | ||||
|         this.areaName = e.name | ||||
|         this.search.current = 1 | ||||
|  | ||||
|         this.$nextTick(() => { | ||||
|           this.getList() | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       getTree () { | ||||
|         this.instance.post(`/admin/area/queryAllArea?id=${this.user.info.areaId}`).then(res => { | ||||
|           if (res.code === 0) { | ||||
|             let parent = res.data.map(v => { | ||||
|               v.label = v.name | ||||
|               v.children = [] | ||||
|  | ||||
|               return v | ||||
|             }).filter(e => !e.parentid)[0] | ||||
|             this.defaultExpanded = [parent.id] | ||||
|             this.defaultChecked = [parent.id] | ||||
|             this.search.areaId = parent.id | ||||
|             this.addChild(parent, res.data) | ||||
|             this.areaTree = [parent] | ||||
|  | ||||
|             this.$nextTick(() => { | ||||
|               this.$refs.tree.setCurrentKey(parent.id) | ||||
|             }) | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       addChild (parent, list) { | ||||
|         for (let i = 0; i < list.length; i++) { | ||||
|           if (list[i].parentId === parent.id) { | ||||
|             parent.children.push(list[i]) | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         if (list.length > 0) { | ||||
|           parent['children'].map(v => this.addChild(v, list)) | ||||
|         } | ||||
|       }, | ||||
|  | ||||
|       remove(id) { | ||||
|         this.$confirm('确定删除该数据?').then(() => { | ||||
|           this.instance.post(`/app/appeveryvillagecode/delete?ids=${id}`).then(res => { | ||||
|             if (res.code == 0) { | ||||
|               this.$message.success('删除成功!') | ||||
|               this.getList() | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       toAdd(id) { | ||||
|         this.$emit('change', { | ||||
|           type: 'Add', | ||||
|           params: { | ||||
|             areaName: this.areaName, | ||||
|             id: id || '', | ||||
|             areaId: this.search.areaId | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .villagecode { | ||||
|   .table-tags { | ||||
|     .el-tag { | ||||
|       margin-right: 8px; | ||||
|  | ||||
|       &:last-child { | ||||
|         margin-right: 0; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .addressBook-left__list { | ||||
|     height: calc(100% - 40px); | ||||
|     padding: 8px 8px; | ||||
|     overflow: auto; | ||||
|  | ||||
|     .addressBook-left__tags--item { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       justify-content: space-between; | ||||
|       height: 40px; | ||||
|       padding: 0 8px 0 16px; | ||||
|       color: #222222; | ||||
|  | ||||
|       &.addressBook-left__tags--item-active, &:hover { | ||||
|         background: #E8EFFF; | ||||
|         color: #2266FF; | ||||
|  | ||||
|         i, span { | ||||
|           color: #2266FF; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       span { | ||||
|         font-size: 14px; | ||||
|       } | ||||
|  | ||||
|       i { | ||||
|         cursor: pointer; | ||||
|         color: #8e9ebf; | ||||
|         font-size: 16px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .addressBook-left__list--title { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       margin-bottom: 8px; | ||||
|  | ||||
|       .addressBook-left__list--search { | ||||
|         flex: 1; | ||||
|         :deep( input ){ | ||||
|           width: 100%; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .el-button { | ||||
|         width: 84px; | ||||
|         flex-shrink: 1; | ||||
|         margin-right: 8px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     span { | ||||
|       color: #222222; | ||||
|       font-size: 14px; | ||||
|     } | ||||
|  | ||||
|     :deep( .el-tree ){ | ||||
|       background: transparent; | ||||
|  | ||||
|       .el-tree-node__expand-icon.is-leaf { | ||||
|         color: transparent!important; | ||||
|       } | ||||
|  | ||||
|       .el-tree-node__content > .el-tree-node__expand-icon { | ||||
|         padding: 4px; | ||||
|       } | ||||
|  | ||||
|       .el-tree-node__content { | ||||
|         height: 32px; | ||||
|       } | ||||
|  | ||||
|       .el-tree__empty-text { | ||||
|         color: #222; | ||||
|         font-size: 14px; | ||||
|       } | ||||
|  | ||||
|       .el-tree-node__children .el-tree-node__content { | ||||
|         height: 32px; | ||||
|       } | ||||
|  | ||||
|       .el-tree-node__content:hover { | ||||
|         background: #E8EFFF; | ||||
|         color: #222222; | ||||
|         border-radius: 2px; | ||||
|       } | ||||
|  | ||||
|       .is-current > .el-tree-node__content { | ||||
|         &:hover { | ||||
|           background: #2266FF; | ||||
|           color: #fff; | ||||
|         } | ||||
|  | ||||
|         background: #2266FF; | ||||
|  | ||||
|         span { | ||||
|           color: #fff; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .villagecode-left { | ||||
|     width: 100%; | ||||
|     height: auto; | ||||
|     background: #FAFAFB; | ||||
|  | ||||
|     .villagecode-left__title { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       height: 40px; | ||||
|       padding: 0 16px; | ||||
|       background: #E5E5E5; | ||||
|  | ||||
|       h2 { | ||||
|         color: #222; | ||||
|         font-size: 14px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .villagecode-left__list { | ||||
|       height: calc(100% - 40px); | ||||
|       padding: 8px 0; | ||||
|       overflow: auto; | ||||
|  | ||||
|       span { | ||||
|         display: block; | ||||
|         height: 40px; | ||||
|         line-height: 40px; | ||||
|         padding: 0 24px; | ||||
|         color: #222222; | ||||
|         font-size: 14px; | ||||
|         cursor: pointer; | ||||
|         border-right: 2px solid transparent; | ||||
|         background: transparent; | ||||
|  | ||||
|         &:hover { | ||||
|           color: #2266FF; | ||||
|           background: #E8EFFF; | ||||
|         } | ||||
|  | ||||
|         &.left-active { | ||||
|           color: #2266FF; | ||||
|           border-color: #2266FF; | ||||
|           background: #E8EFFF; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   :deep( .ai-list__content--right ){ | ||||
|  | ||||
|     .ai-list__content--right-wrapper { | ||||
|       min-height: 100%; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
| .message-info__img { | ||||
|   font-size: 0; | ||||
|   width: 144px; | ||||
|   height: 144px; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										70
									
								
								src/App.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/App.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| <template> | ||||
|   <div id="app" :class="{greyFilter,[`theme-${$theme}`]:true}"> | ||||
|     <router-view/> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapState} from "vuex"; | ||||
| import customConfig from "./config.json"; | ||||
|  | ||||
| export default { | ||||
|   name: 'app', | ||||
|   computed: { | ||||
|     ...mapState(['sys']), | ||||
|     greyFilter: v => v.sys?.theme?.enableGreyFilter == 1, | ||||
|   }, | ||||
|   methods: { | ||||
|     initFavicon(icon) { | ||||
|       const linkList = document.head.querySelectorAll('link') || {}, | ||||
|           url = `${this.$cdn}/favicon/${icon || "favicon"}.ico` | ||||
|       if (Object.values(linkList).findIndex(e => e.href == url) == -1) { | ||||
|         let favicon = document.createElement("link") | ||||
|         favicon.rel = "icon" | ||||
|         favicon.href = url | ||||
|         document.head.appendChild(favicon) | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.initFavicon(customConfig.sysInfo?.favicon) | ||||
|     document.title = this.sys.info.fullTitle | ||||
|     customConfig?.hmt && this.$injectLib("https://hm.baidu.com/hm.js?4e5dd7c5512e5da68c025c3b956fbd5d") | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss"> | ||||
| html, body { | ||||
|   height: 100%; | ||||
|   width: 100%; | ||||
|   padding: 0; | ||||
|   margin: 0; | ||||
| } | ||||
|  | ||||
| body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, p, blockquote, th, td { | ||||
|   margin: 0; | ||||
|   padding: 0; | ||||
| } | ||||
|  | ||||
| #app { | ||||
|   font-family: Helvetica, Arial, sans-serif; | ||||
|   -webkit-font-smoothing: antialiased; | ||||
|   -moz-osx-font-smoothing: grayscale; | ||||
|   overflow: hidden; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
| } | ||||
|  | ||||
| li { | ||||
|   list-style-type: none; | ||||
| } | ||||
|  | ||||
| .greyFilter { | ||||
|   filter: grayscale(100%); | ||||
| } | ||||
|  | ||||
| .amap-logo, .amap-copyright { | ||||
|   display: none !important; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										
											BIN
										
									
								
								src/assets/bg_prolife.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/bg_prolife.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 94 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/building.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/building.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 22 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/loginLeft.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/loginLeft.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 616 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/loginRightBottom.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/loginRightBottom.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 23 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/loginRightTop.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/loginRightTop.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 15 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/nav_bg.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/nav_bg.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 75 KiB | 
							
								
								
									
										125
									
								
								src/components/AiAreaPicker.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								src/components/AiAreaPicker.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | ||||
| <template> | ||||
|   <section class="AiAreaPicker"> | ||||
|     <div @click="handleOpenDialog"> | ||||
|       <slot v-if="$scopedSlots.default" :selected="selected"/> | ||||
|       <el-button v-else type="text">选择地区</el-button> | ||||
|     </div> | ||||
|     <ai-dialog :visible.sync="dialog" title="选择地区" @onConfirm="submit" @close="selecting=[],init()" destroy-on-close> | ||||
|       <el-breadcrumb separator-class="el-icon-arrow-right"> | ||||
|         <el-breadcrumb-item v-for="(item,i) in path" :key="item.id"> | ||||
|           <el-button type="text" @click.native="handlePathClick(i)">{{ item.name }}</el-button> | ||||
|         </el-breadcrumb-item> | ||||
|       </el-breadcrumb> | ||||
|       <ai-table-select class="mar-t16" v-model="selecting" :instance="instance" :action="action" :isShowPagination="false" extra="hidden" search-key="name" | ||||
|                        multiple valueObj> | ||||
|         <template slot="extra" slot-scope="{row}"> | ||||
|           <el-button v-if="row.id!=root" type="text" icon="el-icon-arrow-right" @click.stop="getChildren(row)"/> | ||||
|         </template> | ||||
|       </ai-table-select> | ||||
|     </ai-dialog> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|  | ||||
| export default { | ||||
|   name: "AiAreaPicker", | ||||
|   model: { | ||||
|     prop: "value", | ||||
|     event: "change" | ||||
|   }, | ||||
|   props: { | ||||
|     value: {default: ""}, | ||||
|     meta: {default: null}, | ||||
|     root: {default: ""}, | ||||
|     instance: {type: Function, required: true}, | ||||
|     multiple: Boolean | ||||
|   }, | ||||
|   computed: { | ||||
|     action() { | ||||
|       let currentParent = this.path.slice(-1)?.[0]?.id | ||||
|       return !!currentParent && /[^0]0{0,2}$/.test(currentParent) ? `/admin/appresident/queryAreaIdGroup?currentAreaId=${currentParent}` : | ||||
|           `/admin/area/queryAreaByParentIdSelf?self=${currentParent == this.root ? 1 : ''}&id=${currentParent}` | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     value(v) { | ||||
|       this.dispatch('ElFormItem', 'el.form.change', [v]); | ||||
|     }, | ||||
|     selected(v) { | ||||
|       this.$emit("update:meta", v) | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       dialog: false, | ||||
|       options: [], | ||||
|       selecting: [], | ||||
|       path: [], | ||||
|       selected: [] | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     dispatch(componentName, eventName, params) { | ||||
|       let parent = this.$parent || this.$root; | ||||
|       let name = parent.$options.componentName; | ||||
|       while (parent && (!name || name !== componentName)) { | ||||
|         parent = parent.$parent; | ||||
|         if (parent) { | ||||
|           name = parent.$options.componentName; | ||||
|         } | ||||
|       } | ||||
|       if (parent) { | ||||
|         parent.$emit.apply(parent, [eventName].concat(params)); | ||||
|       } | ||||
|     }, | ||||
|     getChildren(row) { | ||||
|       let area = this.path.find(e => e.id == row.id) | ||||
|       if (!area) this.path.push(row) | ||||
|     }, | ||||
|     handlePathClick(index) { | ||||
|       this.path.splice(index + 1, 10) | ||||
|     }, | ||||
|     submit() { | ||||
|       this.$emit("change", this.selecting.map(e => e.id)) | ||||
|       this.selected = this.$copy(this.selecting) | ||||
|       this.$emit("select", this.selecting) | ||||
|       this.dialog = false | ||||
|     }, | ||||
|     init() { | ||||
|       this.path = [{id: this.root, name: "可选范围"}] | ||||
|     }, | ||||
|     handleOpenDialog() { | ||||
|       let areas = this.value.filter(e => /^\d{12}$/.test(e)) | ||||
|       this.instance.post("/admin/area/getAreaNameByids", null, { | ||||
|         params: {ids: areas?.toString()} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.selecting = this.value.map(id => ({id, name: res.data?.[id] || id})) | ||||
|           this.dialog = true | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     getAreaById(id) { | ||||
|       return this.instance.post("/admin/area/queryAreaByAreaid", null, { | ||||
|         params: {id} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           return res.data | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.init() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AiAreaPicker { | ||||
|   .mar-t16 { | ||||
|     margin-top: 16px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -1,7 +1,6 @@ | ||||
| <template> | ||||
|   <section class="AppLicence"> | ||||
|   <section class="AppLicence" v-if="showLicence"> | ||||
|     <ai-list> | ||||
|       <ai-title slot="title" title="产品许可" isShowBottomBorder  :instance="instance"></ai-title> | ||||
|       <template #content> | ||||
|         <div class="licence-content"> | ||||
|           <img class="left-img" src="https://cdn.cunwuyun.cn/dvcp/key.png" alt="" /> | ||||
| @@ -30,13 +29,14 @@ | ||||
|               <span class="value">{{info.ip}}</span> | ||||
|             </div> | ||||
|             <el-upload | ||||
|               class="upload-demo" | ||||
|               class="upload-demo mar-r16" | ||||
|               action | ||||
|               multiple | ||||
|               accept=".lic" | ||||
|               :http-request="uploadFile"> | ||||
|               <div class="btn">上传许可</div> | ||||
|             </el-upload> | ||||
|             <div class="btn" @click="showLicence = false">返回登录</div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </template> | ||||
| @@ -50,19 +50,25 @@ export default { | ||||
|   label: "产品许可", | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       files: [], | ||||
|       info: {} | ||||
|       info: {}, | ||||
|       showLicence: false | ||||
|     } | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.getDetail() | ||||
| 
 | ||||
|   }, | ||||
|   methods: { | ||||
|     show() { | ||||
|       this.showLicence = true | ||||
|       this.getDetail() | ||||
|     }, | ||||
|     hide() { | ||||
|       this.showLicence = false | ||||
|     }, | ||||
|     getDetail() { | ||||
|       this.instance.post(`/admin/license/detail`).then(res => { | ||||
|         if (res?.data) { | ||||
| @@ -78,7 +84,9 @@ export default { | ||||
|           this.$message.success("证书上传成功!"); | ||||
|           this.getDetail() | ||||
|         } | ||||
|       }); | ||||
|       }).catch((err) => { | ||||
|         this.$message.error(err); | ||||
|       }) | ||||
|     }, | ||||
|   }, | ||||
| 
 | ||||
| @@ -88,6 +96,10 @@ export default { | ||||
| .AppLicence { | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   position: fixed; | ||||
|   top: 0; | ||||
|   left: 0; | ||||
|   z-index: 99; | ||||
|   :deep( .ai-list){ | ||||
|     background-color: #F5F6F9; | ||||
|   } | ||||
| @@ -102,7 +114,8 @@ export default { | ||||
|   } | ||||
|   .licence-content{ | ||||
|     display: flex; | ||||
|     margin-top: 30px; | ||||
|     width: 1000px; | ||||
|     margin: 200px auto 0; | ||||
|     .left-img{ | ||||
|       width: 200px; | ||||
|       height: 200px; | ||||
| @@ -111,7 +124,6 @@ export default { | ||||
|       width: 800px; | ||||
|       .title{ | ||||
|         font-size: 24px; | ||||
|         font-family: MicrosoftYaHei-Bold, MicrosoftYaHei; | ||||
|         font-weight: bold; | ||||
|         color: #222; | ||||
|         line-height: 24px; | ||||
| @@ -119,14 +131,12 @@ export default { | ||||
|       } | ||||
|       .mini-title{ | ||||
|         font-size: 14px; | ||||
|         font-family: MicrosoftYaHei; | ||||
|         color: #555; | ||||
|         line-height: 22px; | ||||
|         margin-bottom: 20px; | ||||
|       } | ||||
|       .info{ | ||||
|         font-size: 14px; | ||||
|         font-family: MicrosoftYaHei; | ||||
|         line-height: 22px; | ||||
|         margin-bottom: 8px; | ||||
|         display: flex; | ||||
| @@ -147,7 +157,11 @@ export default { | ||||
|       .mar-b32{ | ||||
|         margin-bottom: 32px; | ||||
|       } | ||||
|       .upload-demo{ | ||||
|         display: inline-block; | ||||
|       } | ||||
|       .btn{ | ||||
|         display: inline-block; | ||||
|         width: 88px; | ||||
|         height: 32px; | ||||
|         line-height: 32px; | ||||
| @@ -158,6 +172,9 @@ export default { | ||||
|         color: #fff; | ||||
|         font-size: 14px; | ||||
|       } | ||||
|       .mar-r16{ | ||||
|         margin-right: 16px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										255
									
								
								src/components/headerNav.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								src/components/headerNav.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,255 @@ | ||||
| <template> | ||||
|   <div class="headerNav navBg"> | ||||
|     <div style="position: relative"> | ||||
|       <ai-icon type="logo" :icon="system.logo||'iconcunwei'"/> | ||||
|       <ai-icon type="logo" :icon="system.logo||'iconcunwei'" class="textShadow"/> | ||||
|     </div> | ||||
|     <span class="headerTitle">{{ system.fullTitle }}<div class="textShadow" v-html="system.fullTitle"/></span> | ||||
|     <el-row type="flex" align="middle" class="toolbar"> | ||||
|       <!--下载中心--> | ||||
|       <downloan-center-btn v-if="extra.downloadCenter"/> | ||||
|       <!--大屏按钮--> | ||||
|       <dv-btn v-if="extra.dv"/> | ||||
|       <div class="btn" v-if="extra.showTool" @click="home.showTool=false">隐藏工具栏</div> | ||||
|       <div class="btn" v-if="extra.helpDoc" @click="openHelp">帮助文档</div> | ||||
|       <app-qrcode v-if="extra.appQRCode"/> | ||||
|       <div class="btn" v-if="extra.customerService" @click.native="openAiService"> | ||||
|         <div class="iconfont iconCustomer_Service"></div> | ||||
|         <div>智能客服</div> | ||||
|       </div> | ||||
|       <!--推荐链接--> | ||||
|       <link-btn/> | ||||
|     </el-row> | ||||
|     <el-dropdown @visible-change="v=>isClick=v" @command="doMenu" class="rightDropdown"> | ||||
|       <el-row type="flex" align="middle"> | ||||
|         <el-avatar :src="user.info.avatar"> | ||||
|           {{ defaultAvatar }} | ||||
|         </el-avatar> | ||||
|         <span>{{ [user.info.name, user.info.roleName].join(" - ") }}</span> | ||||
|         <i :class="dropdownIcon"/> | ||||
|       </el-row> | ||||
|       <el-dropdown-menu> | ||||
|         <el-dropdown-item command="user">用户中心</el-dropdown-item> | ||||
|         <el-dropdown-item command="signOut">退出</el-dropdown-item> | ||||
|       </el-dropdown-menu> | ||||
|     </el-dropdown> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapState} from "vuex"; | ||||
| import DvBtn from "./headerTools/dvBtn"; | ||||
| import DownloanCenterBtn from "./headerTools/downloanCenterBtn"; | ||||
| import extra from "../config.json" | ||||
| import AppQrcode from "./headerTools/appQrcode"; | ||||
| import LinkBtn from "./headerTools/linkBtn"; | ||||
|  | ||||
| export default { | ||||
|   name: 'headerNav', | ||||
|   components: {LinkBtn, AppQrcode, DownloanCenterBtn, DvBtn}, | ||||
|   data() { | ||||
|     return { | ||||
|       extra, | ||||
|       isClick: false, | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['user', 'sys']), | ||||
|     dropdownIcon() { | ||||
|       return this.isClick ? 'el-icon-caret-top' : 'el-icon-caret-bottom' | ||||
|     }, | ||||
|     defaultAvatar() { | ||||
|       return this.user.info.name?.slice(-2) || "游客" | ||||
|     }, | ||||
|     system: v => v.sys?.info || {} | ||||
|   }, | ||||
|   created() { | ||||
|     this.$dict.load('fileFrom'); | ||||
|   }, | ||||
|   methods: { | ||||
|     // 获取最新的安卓、ios下载二维码 | ||||
|     doMenu(comm) { | ||||
|       switch (comm) { | ||||
|         case 'signOut': | ||||
|           //登出 | ||||
|           this.$confirm("是否要登出?", {type: "warning"}).then(() => { | ||||
|             this.$store.commit("signOut") | ||||
|           }).catch(() => { | ||||
|           }) | ||||
|           break; | ||||
|         case 'user': | ||||
|           this.$router.push({name: "个人中心"}) | ||||
|           break; | ||||
|       } | ||||
|     }, | ||||
|     openAiService() { | ||||
|       window.open('http://v3.faqrobot.org/robot/chat1.html?sysNum=153543696570625098') | ||||
|     }, | ||||
|     openHelp() { | ||||
|       window.open('https://www.yuque.com/books/share/eeaaa5e3-a528-42eb-872e-20d661f3d0e2') | ||||
|     }, | ||||
|     changeStatus(id) { | ||||
|       this.$request.post(`/app/sysuserdownload/addOrUpdate`, { | ||||
|         id, status: "2" | ||||
|       }).then(res => { | ||||
|         if (res?.code == 0) { | ||||
|           this.getFiles(); | ||||
|         } | ||||
|  | ||||
|       }); | ||||
|     }, | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .headerNav { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   width: 100%; | ||||
|   background-repeat: no-repeat; | ||||
|   background-size: 100% 48px; | ||||
|   position: fixed; | ||||
|   z-index: 99; | ||||
|   height: 48px; | ||||
|   padding-left: 24px; | ||||
|   box-sizing: border-box; | ||||
|   top: 0; | ||||
|   color: white; | ||||
|   font-size: 14px; | ||||
|  | ||||
|   .AiIcon { | ||||
|     font-size: 38px; | ||||
|     width: auto; | ||||
|     height: auto; | ||||
|     background: linear-gradient(180deg, #FFFFFF 0%, #CCDBF6 100%); | ||||
|     -webkit-background-clip: text; | ||||
|     -webkit-text-fill-color: transparent; | ||||
|  | ||||
|     &:hover { | ||||
|       color: white; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .headerTitle { | ||||
|     flex: 1; | ||||
|     min-width: 0; | ||||
|     font-size: 24px; | ||||
|     color: #FFF; | ||||
|     line-height: 28px; | ||||
|     background: linear-gradient(180deg, #FFFFFF 0%, #CCDBF6 100%); | ||||
|     -webkit-background-clip: text; | ||||
|     -webkit-text-fill-color: transparent; | ||||
|     font-weight: bold; | ||||
|     margin-left: 8px; | ||||
|     position: relative; | ||||
|   } | ||||
|  | ||||
|   :deep(.toolbar) { | ||||
|     gap: 12px; | ||||
|     margin-right: 32px; | ||||
|  | ||||
|     .btn { | ||||
|       padding: 0 12px; | ||||
|       color: white; | ||||
|  | ||||
|       &:hover { | ||||
|         cursor: pointer; | ||||
|         color: rgba(#fff, .8); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .el-dropdown { | ||||
|     height: 48px; | ||||
|     line-height: 48px; | ||||
|     color: #fff; | ||||
|     padding: 0 12px; | ||||
|  | ||||
|     &:hover { | ||||
|       background-color: rgba(46, 51, 68, .15); | ||||
|       color: white; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .el-image { | ||||
|     margin: 12px 0 12px 16px; | ||||
|   } | ||||
|  | ||||
|   .download-wrapper { | ||||
|     position: relative; | ||||
|  | ||||
|     &:hover .download { | ||||
|       display: flex; | ||||
|     } | ||||
|  | ||||
|     .download { | ||||
|       display: none; | ||||
|       position: absolute; | ||||
|       top: 100%; | ||||
|       left: 12px; | ||||
|       transform: translateX(-90%); | ||||
|       width: auto; | ||||
|       height: auto; | ||||
|       padding: 12px; | ||||
|       background: #fff; | ||||
|       box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.1); | ||||
|       border-radius: 2px; | ||||
|       box-sizing: border-box; | ||||
|       z-index: 999; | ||||
|     } | ||||
|  | ||||
|     .download-item { | ||||
|       text-align: center; | ||||
|  | ||||
|       &:first-child { | ||||
|         margin-right: 13px; | ||||
|       } | ||||
|  | ||||
|       & > img { | ||||
|         width: 105px; | ||||
|         height: 105px; | ||||
|         margin-bottom: 7px; | ||||
|       } | ||||
|  | ||||
|       p { | ||||
|         margin-top: 5px; | ||||
|         font-size: 13px; | ||||
|         color: #333; | ||||
|         text-align: center; | ||||
|       } | ||||
|  | ||||
|       .download-item__middle { | ||||
|         img { | ||||
|           width: 13px; | ||||
|           height: 16px; | ||||
|           vertical-align: sub; | ||||
|         } | ||||
|  | ||||
|         span { | ||||
|           padding-left: 8px; | ||||
|           font-size: 13px; | ||||
|           color: #333; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   :deep(.rightDropdown) { | ||||
|     font-size: 12px; | ||||
|     padding: 0 16px; | ||||
|     height: 48px; | ||||
|     background: rgba(#fff, .1); | ||||
|  | ||||
|     .el-avatar > img { | ||||
|       width: 100%; | ||||
|     } | ||||
|  | ||||
|     .el-row { | ||||
|       gap: 4px; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| </style> | ||||
							
								
								
									
										121
									
								
								src/components/headerTools/appQrcode.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								src/components/headerTools/appQrcode.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| <template> | ||||
|   <section class="appQrcode"> | ||||
|     <div class="btn">手机APP</div> | ||||
|     <div class="download"> | ||||
|       <div class="download-item" v-if="!androidQRcode&&!iosQRcode"><p class="nowarp-text" v-text="`暂未发布`"/></div> | ||||
|       <template v-else> | ||||
|         <div class="download-item" v-if='iosQRcode'> | ||||
|           <img :src="iosQRcode" alt=""/> | ||||
|           <div class="download-item__middle"> | ||||
|             <span class="iconfont iconIOS"></span> | ||||
|             <span>iPhone</span> | ||||
|           </div> | ||||
|           <p>手机扫码下载APP</p> | ||||
|         </div> | ||||
|         <div class="download-item" v-if='androidQRcode'> | ||||
|           <img :src="androidQRcode" alt=""/> | ||||
|           <div class="download-item__middle"> | ||||
|             <span class="iconfont iconAndroid"></span> | ||||
|             <span>Android</span> | ||||
|           </div> | ||||
|           <p>手机扫码下载APP</p> | ||||
|         </div> | ||||
|       </template> | ||||
|     </div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "appQrcode", | ||||
|   data() { | ||||
|     return { | ||||
|       iosQRcode: null, | ||||
|       androidQRcode: null | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getAppQRcode() { | ||||
|       this.$request.post(`/admin/sysversion/getLatestIosVersion`, null, { | ||||
|         params: {type: 3} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.iosQRcode = res.data.qrCodeUrl | ||||
|         } | ||||
|       }) | ||||
|  | ||||
|       this.$request.post(`/admin/sysversion/getLatestVersion`, null, { | ||||
|         params: {type: 1} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.androidQRcode = res.data.qrCodeUrl | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|   }, | ||||
|   created() { | ||||
|     this.getAppQRcode() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .appQrcode { | ||||
|   position: relative; | ||||
|  | ||||
|   &:hover .download { | ||||
|     display: flex; | ||||
|   } | ||||
|  | ||||
|   .download { | ||||
|     display: none; | ||||
|     position: absolute; | ||||
|     top: 100%; | ||||
|     left: 12px; | ||||
|     transform: translateX(-90%); | ||||
|     width: auto; | ||||
|     height: auto; | ||||
|     padding: 12px; | ||||
|     background: #fff; | ||||
|     box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.1); | ||||
|     border-radius: 2px; | ||||
|     box-sizing: border-box; | ||||
|     z-index: 999; | ||||
|   } | ||||
|  | ||||
|   .download-item { | ||||
|     text-align: center; | ||||
|  | ||||
|     &:first-child { | ||||
|       margin-right: 13px; | ||||
|     } | ||||
|  | ||||
|     & > img { | ||||
|       width: 105px; | ||||
|       height: 105px; | ||||
|       margin-bottom: 7px; | ||||
|     } | ||||
|  | ||||
|     p { | ||||
|       margin-top: 5px; | ||||
|       font-size: 13px; | ||||
|       color: #333; | ||||
|       text-align: center; | ||||
|     } | ||||
|  | ||||
|     .download-item__middle { | ||||
|       img { | ||||
|         width: 13px; | ||||
|         height: 16px; | ||||
|         vertical-align: sub; | ||||
|       } | ||||
|  | ||||
|       span { | ||||
|         padding-left: 8px; | ||||
|         font-size: 13px; | ||||
|         color: #333; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										221
									
								
								src/components/headerTools/downloanCenterBtn.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								src/components/headerTools/downloanCenterBtn.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,221 @@ | ||||
| <template> | ||||
|   <section class="downloanCenterBtn"> | ||||
|     <el-badge :value="badgeNum" :hidden='badgeNum==0'> | ||||
|       <div class="btn" @click="openDrawer()">下载中心</div> | ||||
|     </el-badge> | ||||
|     <el-drawer title="下载中心" :visible.sync="drawer" :modal-append-to-body="false" size="520"> | ||||
|       <div class="downLoad_main"> | ||||
|         <div class="search_top "> | ||||
|           <p style="color:#999999;">仅显示最近90天的记录</p> | ||||
|           <el-input size="mini" v-model="fileName" placeholder="文件名" clearable prefix-icon="iconfont iconSearch" | ||||
|                     style="width:240px;" @change="getFiles()"/> | ||||
|         </div> | ||||
|         <ul class="infinite-list"> | ||||
|           <li v-for="(item,i) in filesList" class="infinite-list-item " :key="i"> | ||||
|             <div class="left"> | ||||
|               <svg class="svg" aria-hidden="true"> | ||||
|                 <use xlink:href="#iconZip"/> | ||||
|               </svg> | ||||
|             </div> | ||||
|             <div class="middle"> | ||||
|               <p class="fileName">{{ item.fileName }}【密码:{{ item.pwd }}】</p> | ||||
|               <p> | ||||
|                 <span>来源:</span> | ||||
|                 <span>{{ $dict.getLabel('fileFrom', item.fileFrom) }}</span> | ||||
|                 <span>{{ (item.size / 1000).toFixed(2) + "KB" }}</span> | ||||
|                 <span>{{ item.createTime }}</span> | ||||
|               </p> | ||||
|             </div> | ||||
|             <div class="right"> | ||||
|               <span class="iconfont iconResetting" v-if="item.status==0">处理中</span> | ||||
|               <span v-if="item.status==2">已下载</span> | ||||
|               <i class="iconfont iconDownload" @click="downFile(item)" v-if="item.status!=0">下载</i> | ||||
|             </div> | ||||
|           </li> | ||||
|         </ul> | ||||
|       </div> | ||||
|     </el-drawer> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapState} from "vuex"; | ||||
|  | ||||
| export default { | ||||
|   name: "downloanCenterBtn", | ||||
|   data() { | ||||
|     return { | ||||
|       badgeNum: 0, | ||||
|       drawer: false,//抽屉 | ||||
|       filesList: [], | ||||
|       fileName: '', | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['user']) | ||||
|   }, | ||||
|   methods: { | ||||
|     openDrawer() { | ||||
|       this.drawer = true; | ||||
|       this.getFiles(); | ||||
|     }, | ||||
|     getFiles() { | ||||
|       this.$request.post(`/app/sysuserdownload/list`, null, { | ||||
|         params: { | ||||
|           userId: this.user.info.id, | ||||
|           fileName: this.fileName, | ||||
|           current: 1, | ||||
|           size: 1000, | ||||
|         } | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.filesList = res.data.records; | ||||
|           this.searchNum() | ||||
|         } | ||||
|       }); | ||||
|     }, | ||||
|     //查询未完成数量 | ||||
|     searchNum() { | ||||
|       this.$request.post(`/app/sysuserdownload/queryCountByUserId`, null, { | ||||
|         params: { | ||||
|           userId: this.user.info.id | ||||
|         } | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.badgeNum = res.data; | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     downFile(item) { | ||||
|       this.changeStatus(item.id); | ||||
|       // window.open(item.accessUrl); | ||||
|       let elemIF = document.createElement('iframe'); | ||||
|       elemIF.src = item.accessUrl; | ||||
|       elemIF.style.display = 'none'; | ||||
|       document.body.appendChild(elemIF); | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.searchNum() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .downloanCenterBtn { | ||||
|   .downLoad_main { | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     padding: 16px; | ||||
|     box-sizing: border-box; | ||||
|  | ||||
|     .search_top { | ||||
|       display: flex; | ||||
|       justify-content: space-between; | ||||
|       align-items: center; | ||||
|       padding-bottom: 8px; | ||||
|     } | ||||
|  | ||||
|     .infinite-list { | ||||
|       width: 100%; | ||||
|       height: 100%; | ||||
|  | ||||
|       .infinite-list-item { | ||||
|         width: 100%; | ||||
|         padding: 8px; | ||||
|         box-sizing: border-box; | ||||
|         background: rgba(255, 255, 255, 1); | ||||
|         border-radius: 4px; | ||||
|         border: 1px solid rgba(208, 212, 220, 1); | ||||
|         margin-bottom: 8px; | ||||
|         display: flex; | ||||
|         justify-content: space-between; | ||||
|  | ||||
|         .left { | ||||
|           display: flex; | ||||
|           justify-content: center; | ||||
|           align-items: center; | ||||
|           width: 30px; | ||||
|  | ||||
|           .svg { | ||||
|             width: 24px; | ||||
|             height: 24px; | ||||
|             vertical-align: middle; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         .middle { | ||||
|           flex: 1; | ||||
|  | ||||
|           .fileName { | ||||
|             color: #333333; | ||||
|             font-size: 14px; | ||||
|           } | ||||
|  | ||||
|           p:nth-child(2) { | ||||
|             color: #999999; | ||||
|             font-size: 12px; | ||||
|  | ||||
|             span { | ||||
|               padding: 0 4px; | ||||
|             } | ||||
|  | ||||
|             span:nth-child(2) { | ||||
|               border-right: solid 1px #999999; | ||||
|             } | ||||
|  | ||||
|             span:nth-child(3) { | ||||
|               border-right: solid 1px #999999; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         .right { | ||||
|           display: flex; | ||||
|           justify-content: center; | ||||
|           align-items: center; | ||||
|           font-size: 12px; | ||||
|           width: 90px; | ||||
|           text-align: center; | ||||
|  | ||||
|           span { | ||||
|             color: #999999; | ||||
|           } | ||||
|  | ||||
|           i { | ||||
|             display: block; | ||||
|             width: 50px; | ||||
|             color: #5088FF; | ||||
|             font-size: 12px; | ||||
|             cursor: pointer; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|       } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     ::-webkit-scrollbar { | ||||
|       width: 4px; | ||||
|       background-color: #eee; | ||||
|     } | ||||
|  | ||||
|     ::-webkit-scrollbar-thumb { | ||||
|       background-color: #8888; | ||||
|  | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| :deep(.el-drawer__wrapper) { | ||||
|   position: fixed; | ||||
|   width: 100%; | ||||
|   top: 0; | ||||
|   bottom: 0; | ||||
|   right: 0; | ||||
|  | ||||
|   .el-drawer__header > span:focus { | ||||
|     outline: 0 | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										52
									
								
								src/components/headerTools/dvBtn.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/components/headerTools/dvBtn.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| <template> | ||||
|   <section class="dvBtn"> | ||||
|     <el-popover title="数据大屏" width="500" trigger="click"> | ||||
|       <div flex class="wrap"> | ||||
|         <div class="el-button--text pad-r8 pad-b8" style="width: 50%" v-for="op in dvOptions" :key="op.id" v-text="op.name||'无名大屏'" | ||||
|              @click="handleOpenDV(op.id)"/> | ||||
|       </div> | ||||
|       <div slot="reference" class="btn">数据大屏</div> | ||||
|     </el-popover> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import extra from "../../config.json" | ||||
|  | ||||
| export default { | ||||
|   name: "dvBtn", | ||||
|   data() { | ||||
|     return { | ||||
|       dvOptions: [] | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getDvList() { | ||||
|       this.$request.post("/app/appdiylargescreen/allLargeScreenProjectByPage", null, { | ||||
|         params: {size: 9999, status: 1} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.dvOptions = res.data.records | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleOpenDV(id) { | ||||
|       window.open(`${location.origin}${extra.base || ""}/dv?id=${id}#dv`) | ||||
|     }, | ||||
|   }, | ||||
|   created() { | ||||
|     this.getDvList() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .dvBtn { | ||||
|  | ||||
| } | ||||
|  | ||||
| :deep(.el-button--text) { | ||||
|   cursor: pointer; | ||||
|   user-select: none; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										46
									
								
								src/components/headerTools/linkBtn.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/components/headerTools/linkBtn.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| <template> | ||||
|   <section class="linkBtn"> | ||||
|     <el-dropdown v-if="links.length > 0" @command="handleOpenLink"> | ||||
|       <div class="btn">友情链接</div> | ||||
|       <el-dropdown-menu> | ||||
|         <el-dropdown-item v-for="op in links" :key="op.id" :command="op.url"> | ||||
|           {{ op.title }} | ||||
|         </el-dropdown-item> | ||||
|       </el-dropdown-menu> | ||||
|     </el-dropdown> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "linkBtn", | ||||
|   label: "友情链接", | ||||
|   data() { | ||||
|     return { | ||||
|       links: [] | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getLinks() { | ||||
|       this.$request.post("/app/appwebnavurl/list", null, { | ||||
|         params: {size: 9999, status: 1} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.links = res.data.records | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleOpenLink(url) { | ||||
|       window.open(url) | ||||
|     }, | ||||
|   }, | ||||
|   created() { | ||||
|     this.getLinks() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .linkBtn { | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										40
									
								
								src/components/mainContent.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/components/mainContent.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| <template> | ||||
|   <section class="mainContent"> | ||||
|     <ai-nav-tab :fixed="homePage" :routes="routes"/> | ||||
|     <router-view v-if="refresh"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapState} from "vuex"; | ||||
|  | ||||
| export default { | ||||
|   name: "mainContent", | ||||
|   computed: { | ||||
|     ...mapState(['user', 'homePage']), | ||||
|     routes: v => v.user.info?.menuSet?.map(e => ({...e, label: e.name, name: e.id})) | ||||
|   }, | ||||
|   watch: { | ||||
|     $route(v, old) { | ||||
|       if (v.meta == old.meta && v.fullPath != old.fullPath) { | ||||
|         this.refresh = false | ||||
|         this.$nextTick(() => this.refresh = true) | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       refresh: true | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .mainContent { | ||||
|   height: 100%; | ||||
|   width: 100%; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										296
									
								
								src/components/sliderNav.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								src/components/sliderNav.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,296 @@ | ||||
| <template> | ||||
|   <section class="sliderNav"> | ||||
|     <el-input class="searchApp" size="small" v-model="searchApp" placeholder="搜索应用" clearable | ||||
|               prefix-icon="iconfont iconSearch" @change="handleSearchApp"/> | ||||
|     <el-scrollbar class="ai-menu"> | ||||
|       <div v-for="(item,i) in navs" :key="i"> | ||||
|         <div class="rootMenu" :class="{isActive:menuPath.includes(item.id)}" @click.stop="openKidMenu(item)"> | ||||
|           <i :class="item.style||'iconfont iconloudongmoxing'"/> | ||||
|           <span class="fill mar-l8" v-text="item.name"/> | ||||
|           <i v-if="hasChildren(item.children)" class="iconfont mar-l8" :class="arrowIcon(item.showChildren)"/> | ||||
|         </div> | ||||
|         <div class="kidMenu" v-if="hasChildren(item.children)&&item.showChildren" @click.stop> | ||||
|           <div v-for="menu in item.children" :key="menu.id"> | ||||
|             <div class="submenu wrap pad-l16 pad-r16" flex v-if="hasChildren(menu.children)"> | ||||
|               <b v-text="menu.name" :class="{menuBtn:menu.type==1,current:menuPath.includes(menu.id)}" | ||||
|                  @click="handleSelect(menu)"/> | ||||
|               <div class="menuBtn" v-for="kid in menu.children" :key="kid.id" v-text="kid.name" :title="kid.name" | ||||
|                    @click="handleSelect(kid)" :class="{current:menuPath.includes(kid.id)}"/> | ||||
|             </div> | ||||
|             <div v-else class="lv2Btn" v-text="menu.name" @click="handleSelect(menu)" :class="{current:menuPath.includes(menu.id)}"/> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="divider"/> | ||||
|     </el-scrollbar> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapGetters} from "vuex"; | ||||
| import qs from "querystring"; | ||||
|  | ||||
| export default { | ||||
|   name: "sliderNav", | ||||
|   data() { | ||||
|     return { | ||||
|       menuList: [], | ||||
|       searchApp: "", | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapGetters(['mods']), | ||||
|     navs: v => v.sortList(v.menuList), | ||||
|     menuPath() { | ||||
|       let paths = [], current = this.mods?.find(e => e.route == this.$route.name) | ||||
|       const findParent = id => { | ||||
|         let menu = this.mods?.find(e => e.id == id) | ||||
|         if (menu) { | ||||
|           paths.push(menu.id) | ||||
|           if (!!menu.parentId) findParent(menu.parentId) | ||||
|         } | ||||
|       } | ||||
|       if (current) { | ||||
|         findParent(current.id) | ||||
|       } | ||||
|       return paths | ||||
|     }, | ||||
|     modList: v => v.mods.filter(e => e.isMenu == 1 || e.type == 0 || (e.level > 1 && e.type == 1)) | ||||
|   }, | ||||
|   methods: { | ||||
|     initMenu(menus = this.modList) { | ||||
|       //isMenu 旧版本判断是否为菜单 type<2 新版本判断是否是菜单或应用 | ||||
|       if (menus?.length > 0) { | ||||
|         this.menuList = this.$arr2tree(menus) | ||||
|         this.menuList = this.menuList.map(e => ({ | ||||
|           ...e, | ||||
|           showChildren: this.menuPath.includes(e.id) || !!this.searchApp | ||||
|         })) | ||||
|       } | ||||
|     }, | ||||
|     openKidMenu(parent) { | ||||
|       if (this.hasChildren(parent.children)) { | ||||
|         parent.showChildren = !parent.showChildren | ||||
|       } else { | ||||
|         this.handleSelect(parent) | ||||
|       } | ||||
|     }, | ||||
|     handleSelect(item) { | ||||
|       if (!item.path) return | ||||
|       if (item.route == this.$route.name) { | ||||
|         //避免同一路由跳转的BUG vue-router官方BUG | ||||
|       } else { | ||||
|         let {route: name, path} = item | ||||
|         if (!name) { | ||||
|           this.$message.warning("暂无应用") | ||||
|         } else { | ||||
|           this.goto({name, query: qs.parse(path.split("?")?.[1])}) | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     goto(item) { | ||||
|       return this.$router.push(item) | ||||
|     }, | ||||
|     sortList(list) { | ||||
|       return list?.sort((a, b) => a.showIndex - b.showIndex) || [] | ||||
|     }, | ||||
|     handleSearchApp() { | ||||
|       let {searchApp} = this | ||||
|       if (searchApp) { | ||||
|         let list = this.modList.filter(e => e.name?.indexOf(searchApp) > -1), map = {} | ||||
|         const findParent = e => { | ||||
|           map[e.id] = e | ||||
|           if (!!e.parentId) { | ||||
|             let parent = this.modList.find(m => m.id == e.parentId) | ||||
|             map[parent.id] = parent | ||||
|             if (!!parent.parentId) { | ||||
|               findParent(parent) | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         list.forEach(e => findParent(e)) | ||||
|         console.log(map, list) | ||||
|         this.initMenu(Object.values(map)) | ||||
|       } else { | ||||
|         this.initMenu() | ||||
|       } | ||||
|     }, | ||||
|     arrowIcon(v) { | ||||
|       return v ? "iconArrow_Down" : "iconArrow_Right" | ||||
|     }, | ||||
|     hasChildren(arr) { | ||||
|       return arr?.length > 0 | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.initMenu() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .sliderNav { | ||||
|   width: 200px; | ||||
|   height: 100%; | ||||
|   transition: width .1s; | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   flex-direction: column; | ||||
|   border-right: 1px solid #e5e5e5; | ||||
|   flex-shrink: 0; | ||||
|   box-sizing: border-box; | ||||
|   background: #EFF1F4; | ||||
|   color: #222; | ||||
|   position: relative; | ||||
|   user-select: none; | ||||
|  | ||||
|   .kidMenu { | ||||
|     font-size: 13px; | ||||
|  | ||||
|     .rootName { | ||||
|       font-size: 20px; | ||||
|       color: #333; | ||||
|       cursor: default; | ||||
|     } | ||||
|  | ||||
|     .submenu { | ||||
|       margin-top: 8px; | ||||
|       width: 100%; | ||||
|       color: #aaa; | ||||
|  | ||||
|       & > b { | ||||
|         width: 100%; | ||||
|         line-height: 28px; | ||||
|       } | ||||
|  | ||||
|       & > * { | ||||
|         cursor: default; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .menuBtn { | ||||
|       display: block; | ||||
|       width: 50%; | ||||
|       cursor: pointer; | ||||
|       line-height: 32px; | ||||
|       color: #333; | ||||
|       flex-shrink: 0; | ||||
|       white-space: nowrap; | ||||
|       overflow: hidden; | ||||
|       text-overflow: ellipsis; | ||||
|  | ||||
|       &.line { | ||||
|         width: 100%; | ||||
|       } | ||||
|  | ||||
|       &:hover { | ||||
|         color: #26f; | ||||
|       } | ||||
|  | ||||
|       &.current { | ||||
|         color: #26f; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .rootMenu { | ||||
|     padding: 0 16px; | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     height: 44px; | ||||
|     cursor: pointer; | ||||
|     box-shadow: 0px -1px 0px 0px #D8DCE3 inset, 0px 1px 0px 0px #FFF inset, -1px 0px 0px 0px #E5E5E5 inset; | ||||
|     font-size: 13px; | ||||
|  | ||||
|     .iconfont { | ||||
|       color: #89B; | ||||
|       font-size: 20px; | ||||
|     } | ||||
|  | ||||
|     &.isActive { | ||||
|       color: #26f; | ||||
|  | ||||
|       .iconfont { | ||||
|         color: #26f !important; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     &:hover { | ||||
|       color: #26f; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   :deep(.ai-menu ){ | ||||
|     padding-left: 0; | ||||
|     flex: 1; | ||||
|     min-height: 0; | ||||
|  | ||||
|     .el-scrollbar__wrap { | ||||
|       overflow-x: auto; | ||||
|     } | ||||
|  | ||||
|     &::-webkit-scrollbar { | ||||
|       display: none; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   :deep(.searchApp ){ | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     height: 44px; | ||||
|     padding: 0 16px; | ||||
|     box-shadow: 0px -1px 0px 0px #E5E5E5 inset; | ||||
|  | ||||
|     .el-input__inner { | ||||
|       border: none; | ||||
|       background: inherit; | ||||
|       padding: 0 28px; | ||||
|     } | ||||
|  | ||||
|     .el-input__prefix { | ||||
|       left: 16px; | ||||
|  | ||||
|       .iconSearch { | ||||
|         font-size: 20px; | ||||
|         width: fit-content; | ||||
|         color: #89B; | ||||
|         line-height: 44px; | ||||
|       } | ||||
|  | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .divider { | ||||
|     color: #aaa; | ||||
|     border-top: 1px solid #ddd; | ||||
|     position: relative; | ||||
|     font-size: 12px; | ||||
|     margin: 16px 16px 32px; | ||||
|  | ||||
|     &:before { | ||||
|       content: "到达底部"; | ||||
|       position: absolute; | ||||
|       top: 50%; | ||||
|       left: 50%; | ||||
|       transform: translate(-50%, -50%); | ||||
|       padding: 0 16px; | ||||
|       background: #EFF1F4; | ||||
|       white-space: nowrap; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .lv2Btn { | ||||
|     height: 44px; | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     color: #222; | ||||
|     padding-left: 44px; | ||||
|     cursor: pointer; | ||||
|  | ||||
|     &.current { | ||||
|       background: linear-gradient(90deg, #298BFF 0%, #0C61FF 100%); | ||||
|       box-shadow: inset -1px 0 0 0 #E5E5E5, inset 0 2px 8px 0 #1E4599; | ||||
|       color: #fff; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										33
									
								
								src/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/main.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| import Vue from 'vue'; | ||||
| import App from './App.vue'; | ||||
| import ui from 'element-ui'; | ||||
| import router from './utils/router'; | ||||
| import utils from './utils'; | ||||
| import vcUI from 'dui'; | ||||
| import appComp from '@dui/dv'; | ||||
| import store from './utils/store'; | ||||
| import autoRoutes from "./utils/autoRoutes"; | ||||
| import extra from "./config.json" | ||||
| import axios from "./utils/axios"; | ||||
| //import ob from "dui/lib/js/observer" | ||||
| //备注底座信息,勿删 | ||||
| console.log("欢迎使用%s", extra.sysInfo?.name || "构建版本") | ||||
| //new ob() | ||||
| window.Vue = Vue | ||||
| Vue.use(ui); | ||||
| Vue.use(vcUI); | ||||
| Vue.use(appComp); | ||||
| Vue.config.productionTip = false; | ||||
| Vue.prototype.$cdn = "https://cdn.cunwuyun.cn" | ||||
| Vue.prototype.$request = axios | ||||
| Object.keys(utils).map((e) => (Vue.prototype[e] = utils[e])); | ||||
| const loadPage = () => autoRoutes.init().finally(() => new Vue({router, store, render: h => h(App)}).$mount("#app")) | ||||
| let theme = null | ||||
| store.dispatch('getSystem', extra.sysInfo).then(res => { | ||||
|   theme = JSON.parse(res?.colorScheme || null) | ||||
|   return import(`dui/lib/styles/theme.${theme?.web}.scss`).catch(() => 0) | ||||
| }).finally(() => { | ||||
|   Vue.prototype.$theme = theme?.web || "blue" | ||||
|   !!theme?.web && theme?.web != "blue" ? loadPage() : import(`dui/lib/styles/common.scss`).finally(loadPage) | ||||
| }) | ||||
|  | ||||
							
								
								
									
										107
									
								
								src/utils/autoRoutes.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								src/utils/autoRoutes.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| import {waiting} from "./index"; | ||||
| import router from "./router"; | ||||
| import store from "./store"; | ||||
| import {Message} from "element-ui"; | ||||
| import Vue from "vue"; | ||||
| import extra from "../config.json" | ||||
|  | ||||
| let {state: {user}, commit, dispatch} = store | ||||
| const signOut = () => commit("signOut"), | ||||
|     getUserInfo = () => dispatch("getUserInfo"), | ||||
|     existRoute = route => { | ||||
|       return router.getRoutes()?.find(e => e.name == route?.name || e.path == route?.path) | ||||
|     }, | ||||
|     goto = (route, next) => { | ||||
|       const exist = !!existRoute(route) | ||||
|       return exist ? route.name ? next() : router.replace(route) : | ||||
|           !route.name && route.path == "/" ? router.replace({name: "Home"}).catch(() => 0) : | ||||
|               Message.error("无法找到路由,请联系系统管理员!") | ||||
|     } | ||||
| const loadApps = () => { | ||||
|   //新App的自动化格式 | ||||
|   waiting.init({innerHTML: '应用加载中..'}) | ||||
|   let apps = require.context('../../apps', true, /\.(\/.+)\/App[A-Z][^\/]+\.vue$/, "lazy") | ||||
|   return Promise.all(apps.keys().map(path => apps(path).then(file => { | ||||
|     if (file.default) { | ||||
|       let {name} = file.default | ||||
|       waiting.setContent(`加载${name}...`) | ||||
|       Vue.component(name, file.default) | ||||
|     } else return 0 | ||||
|   }))).then(() => { | ||||
|     waiting.setContent(`正在进入系统...`) | ||||
|     setTimeout(() => waiting.close(), 1000) | ||||
|   }) | ||||
| } | ||||
| const addHome = homePage => { | ||||
|   const component = extra?.homePage || homePage.path | ||||
|   if (extra?.homePage && Vue.component(component)) { | ||||
|     homePage = {...homePage, path: component, component: () => import('../views/mainEntry'), meta: component} | ||||
|   } | ||||
|   router.addRoute('Home', homePage) | ||||
|   router.options.routes[2].children.unshift(homePage) | ||||
|   commit("setHomePage", { | ||||
|     ...homePage, | ||||
|     label: homePage.name, | ||||
|     id: `/v/${component}`, | ||||
|     isMenu: 1, | ||||
|     route: homePage.name, | ||||
|     component, | ||||
|     path: component, | ||||
|   }) | ||||
| } | ||||
| const generateRoutes = (to, from, next) => { | ||||
|   if (router.options.routes[2].children.length > 0) { | ||||
|     goto(to, next) | ||||
|   } else { | ||||
|     Promise.all([getUserInfo(), loadApps()]).then(() => { | ||||
|       //初始化默认工作台 | ||||
|       let homePage = {name: "工作台", path: "console", style: "iconfont iconNav_Dashborad", component: () => import('../views/console')} | ||||
|       addHome(homePage) | ||||
|       const mods = user.info.menuSet?.filter(e => !!e.component)?.map(e => ({route: e.id, ...e})) | ||||
|       mods?.map(({route: name, path, component}) => { | ||||
|         if (!!Vue.component(component) && path && !existRoute({name})) { | ||||
|           let search = path.split("?") | ||||
|           path = search?.[0] || path | ||||
|           const route = {name, path, component: () => import('../views/mainEntry'), meta: component} | ||||
|           router.addRoute('Home', route) | ||||
|           router.options.routes[2].children.push(route) | ||||
|         } | ||||
|       }) | ||||
|       to.name == "Home" ? next({name: homePage.name, replace: true}) : next({...to, replace: true}) | ||||
|     }).then(() => commit("setRoutes", router.options.routes[2].children)) | ||||
|   } | ||||
| } | ||||
| export const routes = [ | ||||
|   {path: "/login", name: "登录", component: () => import('../views/sign')}, | ||||
|   {path: '/dv', name: '数据大屏入口', component: () => import('../views/dvIndex')}, | ||||
|   {path: '/v', name: 'Home', component: () => import('../views/home'), children: []}, | ||||
|   {path: '/', name: "init"}, | ||||
| ] | ||||
| export default { | ||||
|   init: () => { | ||||
|     router.beforeEach((to, from, next) => { | ||||
|       console.log('%s=>%s', from.name, to.name) | ||||
|       if (to.hash == "#pddv") { | ||||
|         const {query} = to | ||||
|         dispatch("getToken", { | ||||
|           username: "18971406276", | ||||
|           password: "admin321!" | ||||
|         }).then(() => next({name: "数据大屏入口", query, hash: "#dv"})) | ||||
|       } else if (["数据大屏入口", "登录"].includes(to.name)) { | ||||
|         next() | ||||
|       } else if (to.hash == "#dv") { | ||||
|         //数据大屏进行的独立页面跳转 | ||||
|         let {query, hash} = to | ||||
|         next({name: "数据大屏入口", query, hash}) | ||||
|       } else if (user.token) { | ||||
|         to.name == "init" ? next({name: "Home"}) : generateRoutes(to, from, next) | ||||
|       } else { | ||||
|         signOut() | ||||
|       } | ||||
|     }) | ||||
|     router.onError(err => { | ||||
|       console.error(err) | ||||
|     }) | ||||
|     return Promise.resolve() | ||||
|   } | ||||
| } | ||||
							
								
								
									
										26
									
								
								src/utils/axios.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/utils/axios.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| import instance from 'dui/lib/js/request' | ||||
| import {Message} from 'element-ui' | ||||
| import extra from "../config.json"; | ||||
| import store from "./store" | ||||
|  | ||||
| let baseURLs = { | ||||
|   production: extra.base || "/", | ||||
|   development: extra.baseURL || '/lan', | ||||
| } | ||||
| instance.defaults.baseURL = baseURLs[process.env.NODE_ENV] | ||||
| instance.interceptors.request.use(config => { | ||||
|   config.timeout = 300000 | ||||
|   if (extra?.isSingleService) { | ||||
|     config.url = config.url.replace(/(app|auth|admin)\//, "api/") | ||||
|   } | ||||
|   if (config.url.startsWith("/node")) { | ||||
|     config.baseURL = "/ns" | ||||
|   } | ||||
|   return config | ||||
| }, error => Message.error(error)) | ||||
| instance.interceptors.response.use(res => res, err => { | ||||
|   if (err?.code == 401) { | ||||
|     store.commit('signOut', 1) | ||||
|   } | ||||
| }) | ||||
| export default instance | ||||
							
								
								
									
										100
									
								
								src/utils/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/utils/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| import {MessageBox} from 'element-ui' | ||||
| import tools from 'dui/lib/js/utils' | ||||
| import store from "./store"; | ||||
|  | ||||
| let {state: {user}} = store | ||||
| const addChildParty = (parent, pending) => { | ||||
|   let doBeforeCount = pending.length | ||||
|   parent["children"] = parent["children"] || [] | ||||
|   pending.map((e, index, arr) => { | ||||
|     if (e.partyOrgParentId == parent.partyOrgId) { | ||||
|       parent.children.push(e) | ||||
|       arr.splice(index, 1) | ||||
|       addChildParty(parent, arr) | ||||
|     } | ||||
|   }) | ||||
|   if (parent.children.length == 0) { | ||||
|     delete parent.children | ||||
|   } | ||||
|   if (pending.length > 0 && doBeforeCount > pending.length) { | ||||
|     parent.children.map(c => addChildParty(c, pending)) | ||||
|   } | ||||
| } | ||||
| /** | ||||
|  * 封装提示框 | ||||
|  */ | ||||
|  | ||||
|  | ||||
| const $confirm = (content, options) => { | ||||
|   return MessageBox.confirm(content, { | ||||
|     type: "warning", | ||||
|     confirmButtonText: "确认", | ||||
|     center: true, | ||||
|     title: "提示", | ||||
|     dangerouslyUseHTMLString: true, | ||||
|     ...options | ||||
|   }) | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 封装权限判断方法 | ||||
|  */ | ||||
|  | ||||
|  | ||||
| const $permissions = flag => { | ||||
|   const buttons = user?.info?.buttons | ||||
|   if (buttons) return buttons.some(b => b.id == flag || b.permission == flag) | ||||
|   else return false | ||||
| } | ||||
|  | ||||
| const $decimalCalc = (...arr) => { | ||||
|   //确认提升精度 | ||||
|   let decimalLengthes = arr.map(e => { | ||||
|     let index = ("" + e).indexOf(".") | ||||
|     return ("" + e).length - index | ||||
|   }) | ||||
|   let maxDecimal = Math.max(...decimalLengthes), precision = Math.pow(10, maxDecimal) | ||||
|   //计算 | ||||
|   let intArr = arr.map(e => (Number(e) || 0) * precision) | ||||
|   //返回计算值 | ||||
|   return intArr.reduce((t, a) => t + a) / precision | ||||
| } | ||||
| export const waiting = { | ||||
|   init(ops, count) { | ||||
|     if (document.body) { | ||||
|       let div = document.createElement('div') | ||||
|       div.id = "ai-waiting" | ||||
|       div.innerHTML = "信息正在加载中..." | ||||
|       div.className = "el-loading-mask is-fullscreen" | ||||
|       div.style.zIndex = '202204271710' | ||||
|       div.style.textAlign = 'center' | ||||
|       div.style.lineHeight = '100vh' | ||||
|       div.style.background = 'rgba(255,255,255,.8)' | ||||
|       div.style.backdropFilter = 'blur(6px)' | ||||
|       document.body.appendChild(div) | ||||
|     } else if (count < 10) { | ||||
|       setTimeout(() => this.init(ops, ++count), 500) | ||||
|     } | ||||
|   }, | ||||
|   getDom() { | ||||
|     return document.querySelector('#ai-waiting') | ||||
|   }, | ||||
|   setContent(html) { | ||||
|     let div = this.getDom() | ||||
|     div.innerHTML = html | ||||
|   }, | ||||
|   close() { | ||||
|     let div = this.getDom() | ||||
|     div.parentElement.removeChild(div) | ||||
|   } | ||||
| } | ||||
| export default { | ||||
|   ...tools, | ||||
|   addChildParty, | ||||
|   $confirm, | ||||
|   $permissions, | ||||
|   $decimalCalc, | ||||
|   $waiting: waiting | ||||
| } | ||||
|  | ||||
|  | ||||
							
								
								
									
										19
									
								
								src/utils/router.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/utils/router.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| import Vue from 'vue' | ||||
| import VueRouter from 'vue-router' | ||||
| import {routes} from "./autoRoutes" | ||||
| import config from "../config.json" | ||||
|  | ||||
| Vue.use(VueRouter) | ||||
| export default new VueRouter({ | ||||
|   base: config.base || '/', | ||||
|   mode: 'history', | ||||
|   hashbang: false, | ||||
|   routes, | ||||
|   scrollBehavior(to) { | ||||
|     if (to.hash) { | ||||
|       return { | ||||
|         selector: to.hash | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| }) | ||||
							
								
								
									
										44
									
								
								src/utils/store.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/utils/store.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| import Vue from 'vue' | ||||
| import Vuex from 'vuex' | ||||
| import preState from 'vuex-persistedstate' | ||||
| import * as modules from "dui/lib/js/modules" | ||||
| import axios from "./axios"; | ||||
| import extra from "../config.json" | ||||
|  | ||||
| Vue.use(Vuex) | ||||
|  | ||||
| export default new Vuex.Store({ | ||||
|   state: { | ||||
|     homePage: {} | ||||
|   }, | ||||
|   mutations: { | ||||
|     setHomePage(state, home) { | ||||
|       state.homePage = home | ||||
|     }, | ||||
|     signOut(state, flag) { | ||||
|       const base = extra.base || "" | ||||
|       if (flag) { | ||||
|         state.user.token = null; | ||||
|         state.user.info = {} | ||||
|         sessionStorage.clear(); | ||||
|         location.href = base + '/login' + location.hash; | ||||
|       } else { | ||||
|         axios.delete('/auth/token/logout').then(() => { | ||||
|           state.user.token = null; | ||||
|           sessionStorage.clear(); | ||||
|           state.user.info = {} | ||||
|           location.href = base + '/login'; | ||||
|         }); | ||||
|       } | ||||
|     }, | ||||
|   }, | ||||
|   getters: { | ||||
|     //后台数据库中的应用集合,在本工程中不一定存在 | ||||
|     mods: state => [ | ||||
|       state.homePage, | ||||
|       state.user.info?.menuSet?.map(e => ({route: e.id, ...e, label: e.name})) | ||||
|     ].flat().filter(Boolean) | ||||
|   }, | ||||
|   modules, | ||||
|   plugins: [preState()] | ||||
| }) | ||||
							
								
								
									
										31
									
								
								src/views/building.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/views/building.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| <template> | ||||
|   <section class="building"> | ||||
|     <div class="title">功能开发中,敬请期待...</div> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "building" | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .building { | ||||
|   position: relative; | ||||
|   height: 100%; | ||||
|   background-image: url("../assets/building.png"); | ||||
|   background-size: 400px 300px; | ||||
|   background-repeat: no-repeat; | ||||
|   background-position: center, center; | ||||
|  | ||||
|   .title { | ||||
|     position: absolute; | ||||
|     top: 50%; | ||||
|     left: 50%; | ||||
|     transform: translate(-50%, -50%); | ||||
|     margin-top: 150px; | ||||
|     font-weight: bold; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										42
									
								
								src/views/console.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/views/console.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| <template> | ||||
|   <section class="console"> | ||||
|     <div class="consoleBg" v-text="`欢迎使用${system.fullTitle}`"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapState} from "vuex"; | ||||
|  | ||||
| export default { | ||||
|   name: "console", | ||||
|   label: "工作台", | ||||
|   computed: { | ||||
|     ...mapState(['sys']), | ||||
|     system: v => v.sys.info || {} | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .console { | ||||
|   height: 100%; | ||||
|  | ||||
|   .consoleBg { | ||||
|     position: absolute; | ||||
|     left: 50%; | ||||
|     top: 50%; | ||||
|     transform: translate(-50%, -50%); | ||||
|     background-image: url("https://cdn.cunwuyun.cn/dvcp/consoleBg.png"); | ||||
|     background-size: 600px 362px; | ||||
|     background-repeat: no-repeat; | ||||
|     background-position: center top; | ||||
|     padding-top: 402px; | ||||
|     font-size: 32px; | ||||
|     font-family: MicrosoftYaHei-Bold, MicrosoftYaHei; | ||||
|     font-weight: bold; | ||||
|     color: #95A1B0; | ||||
|     min-width: 600px; | ||||
|     text-align: center; | ||||
|   } | ||||
|  | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										83
									
								
								src/views/dvIndex.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/views/dvIndex.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| <template> | ||||
|   <section class="dvIndex"> | ||||
|     <ai-dv-wrapper v-model="activeTab" :views="views" :title="title" :theme="theme" v-if="views.length" :background="bgImg" :type="currentStyle" :titleSize="titleSize"> | ||||
|       <ai-dv-viewer urlPrefix="/app" :instance="instance" :dict="dict" :id="currentView.id"/> | ||||
|     </ai-dv-wrapper> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import Vue from "vue"; | ||||
| import {waiting} from "../utils"; | ||||
|  | ||||
| export default { | ||||
|   name: "dvIndex", | ||||
|   provide() { | ||||
|     return { | ||||
|       dv: this | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     currentView: v => v.views.find(e => e.id == v.activeTab) || v.views?.[0] || {}, | ||||
|     background: v => JSON.parse(v.currentView.config || null)?.dashboard?.backgroundImage?.[0]?.url || "", | ||||
|     bgImg: v => v.theme == 1 ? 'https://cdn.cunwuyun.cn/dvcp/dv/img/dj-bg.png' : v.background, | ||||
|     theme() { | ||||
|       if (!this.currentView) return '0' | ||||
|       if (!this.currentView.config) return '0' | ||||
|       const config = JSON.parse(this.currentView.config) | ||||
|       if (config.custom) { | ||||
|         return '0' | ||||
|       } | ||||
|       return config.dashboard.theme | ||||
|     }, | ||||
|     currentStyle: v => JSON.parse(v.currentView.config || null)?.dashboard?.style || "black", | ||||
|     titleSize: v => JSON.parse(v.currentView.config || "{}").dashboard?.titleSize | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       instance: this.$request, | ||||
|       dict: this.$dict, | ||||
|       activeTab: 0, | ||||
|       views: [], | ||||
|       title: "", | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getDvOptions() { | ||||
|       let {id} = this.$route.query | ||||
|       return id ? this.instance.post("/app/appdiylargescreen/queryLargeScreenProjectDetailById", null, { | ||||
|         params: {id, status: 1} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.title = res.data.name | ||||
|           this.views = res.data.lsList?.map(e => ({...e, label: e.title})) | ||||
|         } | ||||
|       }) : Promise.reject() | ||||
|     }, | ||||
|     loadDvs() { | ||||
|       //新App的自动化格式 | ||||
|       waiting.init({innerHTML: '应用加载中..'}) | ||||
|       let apps = require.context('../../apps', true, /\.(\/.+)\/App[A-Z][^\/]+D[Vv]\.vue$/, "lazy") | ||||
|       return Promise.all(apps.keys().map(path => apps(path).then(file => { | ||||
|         if (file.default) { | ||||
|           let {name} = file.default | ||||
|           waiting.setContent(`加载${name}...`) | ||||
|           Vue.component(name, file.default) | ||||
|         } else return 0 | ||||
|       }))).then(() => { | ||||
|         waiting.setContent(`正在进入系统...`) | ||||
|         setTimeout(() => waiting.close(), 1000) | ||||
|       }) | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.loadDvs().then(() => this.getDvOptions()) | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .dvIndex { | ||||
|   height: 100%; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										42
									
								
								src/views/home.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/views/home.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| <template> | ||||
|   <section class="home"> | ||||
|     <header-nav/> | ||||
|     <el-row class="fill" type="flex"> | ||||
|       <slider-nav/> | ||||
|       <main-content class="fill"/> | ||||
|     </el-row> | ||||
|     <ai-copilot v-if="useCopilot" :http="$request"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import SliderNav from "../components/sliderNav"; | ||||
| import MainContent from "../components/mainContent"; | ||||
| import HeaderNav from "../components/headerNav"; | ||||
| import configExtra from "../config.json" | ||||
|  | ||||
| export default { | ||||
|   name: 'app', | ||||
|   components: {HeaderNav, MainContent, SliderNav}, | ||||
|   computed: { | ||||
|     useCopilot: () => !!configExtra?.copilot | ||||
|   }, | ||||
|   created() { | ||||
|     import("../../apps/actions").then(extra => { | ||||
|       const actions = extra?.default || {} | ||||
|       this.$store.hotUpdate({actions}) | ||||
|       Object.keys(actions)?.map(action => this.$store.dispatch(action)) | ||||
|     }).catch(() => 0) | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .home { | ||||
|   height: 100%; | ||||
|   width: 100%; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   padding-top: 48px; | ||||
|   box-sizing: border-box; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										55
									
								
								src/views/mainEntry.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/views/mainEntry.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| <template> | ||||
|   <section class="mainEntry fill"> | ||||
|     <ai-detail list v-if="hasIntro"> | ||||
|       <template #content> | ||||
|         <ai-intro :id="currentApp.guideId" :instance="$request" @start="handleStartUse"/> | ||||
|       </template> | ||||
|     </ai-detail> | ||||
|     <component v-else :is="app" :instance="$request" :dict="$dict" :permissions="$permissions" :menuName="currentApp.name"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|  | ||||
| import Building from "./building"; | ||||
| import Vue from "vue"; | ||||
| import {mapGetters, mapMutations, mapState} from "vuex"; | ||||
|  | ||||
| export default { | ||||
|   name: "mainEntry", | ||||
|   components: {Building}, | ||||
|   computed: { | ||||
|     ...mapState(['logs']), | ||||
|     ...mapGetters(['mods']), | ||||
|     currentApp() { | ||||
|       const {fullPath, name} = this.$route | ||||
|       return this.mods.find(e => !name ? fullPath.indexOf(e.path) > -1 : name == e.route) || Building | ||||
|     }, | ||||
|     app() { | ||||
|       const {currentApp} = this | ||||
|       return Vue.component(currentApp?.component) ? currentApp.component : Building | ||||
|     }, | ||||
|     hasIntro() { | ||||
|       const {app, currentApp, logs} = this | ||||
|       return !!currentApp.guideId && !logs?.closeIntro?.includes(app) | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     ...mapMutations(['addCloseIntro']), | ||||
|     handleStartUse() { | ||||
|       this.addCloseIntro(this.app) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .mainEntry { | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|  | ||||
|   & > * { | ||||
|     height: 100%; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										172
									
								
								src/views/sign.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								src/views/sign.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,172 @@ | ||||
| <template> | ||||
|   <section class="sign"> | ||||
|     <div class="left signLeftBg"> | ||||
|       <el-row type="flex" align="middle"> | ||||
|         <img class="AiIcon" v-if="/[\\\/]/.test(logo.icon)" :src="logo.icon" alt=""/> | ||||
|         <ai-icon v-else type="logo" :icon="logo.icon"/> | ||||
|         <div v-if="logo.text" class="logoText mar-l8" v-text="logo.text"/> | ||||
|       </el-row> | ||||
|       <div class="signLeftContent"> | ||||
|         <div class="titlePane"> | ||||
|           <b v-text="system.name"/> | ||||
|           <div v-text="system.title"/> | ||||
|         </div> | ||||
|         <div class="subTitle" v-for="(t,i) in subTitles" :key="i" v-html="t"/> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="right"> | ||||
|       <div class="projectName mar-b48" :title="system.fullTitle">{{ system.fullTitle }}</div> | ||||
|       <ai-sign v-if="system.edition=='saas'" @login="login" :instance="instance" visible :tps="['wxwork']" :sassLogin="!isDev"/> | ||||
|       <ai-sign v-else isSignIn @login="login" :instance="instance" visible :showScanLogin="system.edition=='standard'||!system.edition"/> | ||||
|       <el-row type="flex" align="middle" class="bottomRecord"> | ||||
|         <div v-if="system.recordDesc" v-text="system.recordDesc"/> | ||||
|         <el-link v-if="system.recordNo" v-text="system.recordNo" :href="system.recordURL"/> | ||||
|         <div v-if="system.ssl" v-html="system.ssl"/> | ||||
|       </el-row> | ||||
|     </div> | ||||
|     <app-licence :instance="instance" ref="licence"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapMutations, mapState} from 'vuex' | ||||
| import AppLicence from "../components/AppLicence"; | ||||
|  | ||||
| export default { | ||||
|   name: "sign", | ||||
|   components: {AppLicence}, | ||||
|   computed: { | ||||
|     ...mapState(['user', 'sys']), | ||||
|     instance: v => v.$request, | ||||
|     system: v => v.sys?.info || {}, | ||||
|     subTitles() { | ||||
|       let list = [ | ||||
|         "构建全域数字大脑,助力政府科学决策", | ||||
|         "全域统一应用入口,移动办公高效协同", | ||||
|         "直接触达居民微信,政民互动“零距离”" | ||||
|       ] | ||||
|       return (typeof this.system.desc == "object" ? this.system.desc : JSON.parse(this.system.desc || null)) || list | ||||
|     }, | ||||
|     logo: v => !!v.system.loginLogo ? {icon: v.system.loginLogo, text: v.system.loginLogoText} : {icon: v.system.logo, text: v.system.logoText}, | ||||
|     isDev: () => process.env.NODE_ENV == "development" | ||||
|   }, | ||||
|   created() { | ||||
|     if (this.user.token) { | ||||
|       this.handleGotoHome() | ||||
|     } else { | ||||
|       const {code, auth_code} = this.$route.query | ||||
|       if (code) { | ||||
|         this.toLogin(code) | ||||
|       } else if (auth_code) { | ||||
|         this.tpLogin(auth_code) | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     ...mapMutations(['setToken']), | ||||
|     login(data) { | ||||
|       if (data.data == '999') { | ||||
|         return this.$refs.licence.show() | ||||
|       } | ||||
|       if (data?.access_token) { | ||||
|         this.setToken([data.token_type, data.access_token].join(" ")) | ||||
|         this.handleGotoHome() | ||||
|       } | ||||
|     }, | ||||
|     handleGotoHome() { | ||||
|       this.$message.success("登录成功!") | ||||
|       if (this.$route.hash == "#dv") { | ||||
|         this.$router.push({name: "数据大屏入口", hash: "#dv"}) | ||||
|       } else { | ||||
|         this.$router.push({name: "Home"}) | ||||
|       } | ||||
|     }, | ||||
|     toLogin(code) { | ||||
|       this.instance.post(`/auth/wechatcp-qr/token`, { | ||||
|         code: code, | ||||
|         type: 'cpuser' | ||||
|       }, { | ||||
|         auth: { | ||||
|           username: 'villcloud', | ||||
|           password: "villcloud" | ||||
|         }, | ||||
|         params: { | ||||
|           grant_type: 'password', | ||||
|           scope: 'server' | ||||
|         } | ||||
|       }).then(this.login) | ||||
|     }, | ||||
|     tpLogin(code) { | ||||
|       this.instance.post("/auth/wechatcp-qr/token", {code}, { | ||||
|         auth: { | ||||
|           username: 'villcloud', | ||||
|           password: "villcloud" | ||||
|         }, | ||||
|         params: { | ||||
|           grant_type: 'password', | ||||
|           scope: 'server' | ||||
|         } | ||||
|       }).then(this.login).catch(() => this.$router.push({})) | ||||
|     } | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .sign { | ||||
|   display: flex; | ||||
|   box-sizing: border-box; | ||||
|   height: 100%; | ||||
|  | ||||
|   .AiIcon { | ||||
|     font-size: 40px; | ||||
|     height: 40px; | ||||
|   } | ||||
|  | ||||
|   .logoText { | ||||
|     font-size: 20px; | ||||
|   } | ||||
|  | ||||
|   :deep(.left ) { | ||||
|     width: 480px; | ||||
|     flex-shrink: 0; | ||||
|     background-size: 100% 100%; | ||||
|     background-repeat: no-repeat; | ||||
|     padding-left: 64px; | ||||
|     padding-top: 40px; | ||||
|     box-sizing: border-box; | ||||
|     color: #fff; | ||||
|     font-size: 16px; | ||||
|  | ||||
|     .iconcunwei1 { | ||||
|       font-size: 36px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   :deep(.right ) { | ||||
|     flex: 1; | ||||
|     min-width: 0; | ||||
|     background-color: #F6F8FB; | ||||
|     background-image: url("../assets/loginRightTop.png"), url("../assets/loginRightBottom.png"); | ||||
|     background-repeat: no-repeat; | ||||
|     background-position: calc(100% - 80px) 0, calc(100% - 40px) 100%; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|  | ||||
|     .bottomRecord { | ||||
|       font-size: 12px; | ||||
|       color: #999; | ||||
|       gap: 16px; | ||||
|       position: fixed; | ||||
|       bottom: 20px; | ||||
|  | ||||
|       .el-link { | ||||
|         font-size: inherit; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| </style> | ||||
| @@ -217,10 +217,10 @@ export default { | ||||
|   &.AiDvWrapper1 { | ||||
|     :deep(.primary) { | ||||
|       .content { | ||||
|         background-image: url("assets/headerThemePrimaryBG_lb.png"), url("assets/headerThemePrimaryBG_rb.png"); | ||||
|         background-image: url("./assets/headerThemePrimaryBG_lb.png"), url("./assets/headerThemePrimaryBG_rb.png"); | ||||
|  | ||||
|         .item { | ||||
|           background-image: url("assets/themeTimeIcon.png"); | ||||
|           background-image: url("./assets/themeTimeIcon.png"); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|   | ||||
| @@ -1,16 +0,0 @@ | ||||
| { | ||||
|   "name": "@dui/dv", | ||||
|   "version": "1.0.0", | ||||
|   "author": "kubbo", | ||||
|   "scripts": { | ||||
|     "lib": "npm unpublish --force&&npm publish" | ||||
|   }, | ||||
|   "main": "index.js", | ||||
|   "dependencies": { | ||||
|     "@amap/amap-jsapi-loader": "^1.0.1", | ||||
|     "@jiaminghi/data-view": "^2.10.0", | ||||
|     "vuedraggable": "^2.24.3", | ||||
|     "element-ui": "^2.13.2", | ||||
|     "vue": "^2.6.11" | ||||
|   } | ||||
| } | ||||
| @@ -11,8 +11,8 @@ $--color-success: $successColor; | ||||
| $--color-warning: $warnColor; | ||||
| $--color-danger: $errorColor; | ||||
| $--color-info: $infoColor; | ||||
| $--font-path: '~element-ui/lib/theme-chalk/fonts'; | ||||
| @import "~element-ui/packages/theme-chalk/src/index"; | ||||
| $--font-path: 'element-ui/lib/theme-chalk/fonts'; | ||||
| @import "element-ui/packages/theme-chalk/src/index"; | ||||
|  | ||||
| /** | ||||
| 常用内外边距样式 | ||||
|   | ||||
| @@ -1,32 +0,0 @@ | ||||
| { | ||||
|   "name": "dui", | ||||
|   "version": "2.0.0", | ||||
|   "author": "kubbo", | ||||
|   "scripts": { | ||||
|     "lib": "npm unpublish --force&&npm publish" | ||||
|   }, | ||||
|   "files": [ | ||||
|     "lib", | ||||
|     "packages" | ||||
|   ], | ||||
|   "main": "packages/index.js", | ||||
|   "dependencies": { | ||||
|     "@amap/amap-jsapi-loader": "^1.0.1", | ||||
|     "@ckeditor/ckeditor5-vue2": "^3.0.1", | ||||
|     "@jiaminghi/data-view": "^2.10.0", | ||||
|     "crypto-js": "^4.1.1", | ||||
|     "dayjs": "^1.8.35", | ||||
|     "html2canvas": "^1.4.1", | ||||
|     "jspdf": "^2.5.1", | ||||
|     "vue-cropper": "^0.5.5", | ||||
|     "vue-qr": "^2.2.1", | ||||
|     "vuedraggable": "^2.24.3", | ||||
|     "element-ui": "^2.13.2", | ||||
|     "vue": "^2.6.11" | ||||
|   }, | ||||
|   "web-types": "docs/web-types.json", | ||||
|   "vetur": { | ||||
|     "tags": "docs/tags.json", | ||||
|     "attributes": "docs/attributes.json" | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user