目录代码整合

This commit is contained in:
aixianling
2022-05-10 20:02:37 +08:00
parent 71049f7f65
commit 036ee91533
324 changed files with 4 additions and 8321 deletions

View File

@@ -0,0 +1,66 @@
<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>

View File

@@ -0,0 +1,203 @@
<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>

View File

@@ -0,0 +1,987 @@
<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>
<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', formart: 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() {
let departId = this.search.departmentId;
if (!departId) departId = 1;
this.btnLoading = true
this.instance.post(`/app/wxcp/wxdepartment/syncDepart`).then(res => {
if (res.code == 0) {
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
})
}
}).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;
::v-deep span {
margin-left: 0;
}
}
.addressBook-left__list--title {
display: flex;
align-items: center;
margin: 8px 8px 0;
.addressBook-left__list--search {
flex: 1;
::v-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;
::v-deep .el-tree {
width: fit-content;
min-width: 100%;
}
::v-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;
}
::v-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;
}
}
}
}
}
::v-deep .ai-list__content--right {
flex: 1;
min-width: 0;
margin-left: 1px;
box-shadow: none;
.ai-list__content--right-wrapper {
width: 100%;
}
}
}
</style>

View File

@@ -0,0 +1,304 @@
<template>
<div class="appcarousel">
<ai-list>
<template slot="title">
<ai-title title="轮播图片" :isShowBottomBorder="false" :isShowArea="false"></ai-title>
</template>
<template slot="content">
<ai-search-bar>
<template slot="left">
<el-button type="primary" icon="iconfont iconAdd" size="small" @click="add">添加</el-button>
</template>
<template slot="right">
<el-input v-model="search.title" size="small" placeholder="搜索标题" clearable
v-throttle="() => {page.current = 1, getList()}"
@clear=";(page.current = 1), (search.title = ''), getList()" suffix-icon="iconfont iconSearch"/>
</template>
</ai-search-bar>
<ai-table :tableData="tableData" :colConfigs="colConfigs" :total="page.total" :current.sync="page.current"
:size.sync="page.size" @getList="getList" class="ai-table">
<!-- 首页封面 -->
<el-table-column label="首页封面" align="left" width="150" slot="imgUrl">
<template slot-scope="{ row }">
<img :src="row.imgUrl" alt="" class="banner-img" v-if="row.imgUrl"/>
</template>
</el-table-column>
<!-- 操作 -->
<el-table-column label="操作" align="center" width="300" slot="option">
<template slot-scope="{ row }">
<el-button type="text" @click="release(row)" v-if="row.status == 1">取消发布</el-button>
<el-button type="text" @click="release(row)" v-else>发布</el-button>
<el-button type="text" @click="detail(row)">详情</el-button>
<el-button type="text" @click="edit(row)">编辑</el-button>
<el-button type="text" @click="remove(row.id)">删除</el-button>
</template>
</el-table-column>
</ai-table>
</template>
</ai-list>
<!-- 添加的模态框 -->
<ai-dialog :title="dialog.title" :visible.sync="visible" @onCancel="visible = false" @onConfirm="addConfirm"
width="800px">
<el-form ref="ruleForm" :model="dialogInfo" :rules="formRules" size="small" label-width="100px">
<!-- 首页封面 -->
<el-form-item label="首页封面" prop="imgUrl">
<ai-uploader v-model="dialogInfo.imgUrl" @change="change" :instance="instance" :limit="1"></ai-uploader>
</el-form-item>
<!-- 活动名称 -->
<el-form-item label="标题" prop="title">
<el-input placeholder="请输入标题" :maxlength="30" show-word-limit v-model="dialogInfo.title"></el-input>
</el-form-item>
<!-- 连接类型 -->
<el-form-item label="链接类型" prop="type">
<ai-select v-model="dialogInfo.type" placeholder="请选择链接类型"
:selectList="$dict.getDict('bannerType')"></ai-select>
</el-form-item>
<!-- 链接 -->
<el-form-item label="链接" prop="linkUrl">
<el-input placeholder="请输入链接" v-model="dialogInfo.linkUrl"></el-input>
</el-form-item>
</el-form>
</ai-dialog>
<!-- 详情的模态框 -->
<ai-dialog title="详情" :visible.sync="detailDialog" customFooter width="800px">
<!-- -->
<el-form ref="ruleForm" :model="dialogInfo" :rules="formRules" size="small" label-width="100px">
<ai-wrapper label-width="80px" :columnsNumber="1">
<ai-info-item label="首页封面">
<span><img :src="dialogInfo.imgUrl" alt="" style="width:100px;height:100px;"/></span>
</ai-info-item>
<ai-info-item label="标题">
<span>{{ dialogInfo.title }}</span>
</ai-info-item>
<ai-info-item label="链接类型">
<span>{{ this.dict.getLabel('bannerType', dialogInfo.type) }}</span>
</ai-info-item>
<ai-info-item label="链接">
<span>{{ dialogInfo.linkUrl }}</span>
</ai-info-item>
</ai-wrapper>
</el-form>
<div class="dialog-footer" slot="footer">
<el-button @click="detailDialog=false">关闭</el-button>
</div>
</ai-dialog>
</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
label: '轮播图片',
name: 'AppCarousel',
// 组件
components: {},
props: {
instance: Function,
dict: Object,
permissions: Function,
},
data() {
return {
visible: false,
detailDialog: false,
// status: 0,
// id:'',
search: {
title: '',
},
tableData: [],
page: {
size: 10,
current: 1,
total: 0,
},
dialog: {
// title: '',
visible: false,
},
dialogInfo: {
id: '',
title: '',
imgUrl: [],
type: '',
linkUrl: '',
},
// fileList: '',
formRules: {
imgUrl: [{required: true, message: '请添加图片', trigger: 'blur'}],
title: [{required: true, message: '请输入标题', trigger: 'blur'}],
type: [{required: true, trigger: 'change', message: '请选择链接类型'}],
// linkUrl: [{ required: true, message: '请输入链接', trigger: 'blur' }]
},
colConfigs: [
{prop: 'imgUrl', label: '首页封面', slot: 'imgUrl'},
{
prop: 'title',
label: '标题',
'show-overflow-tooltip': true,
},
{
prop: 'type',
label: '链接类型',
render: (h, {row}) => {
return h('span', null, this.dict.getLabel('bannerType', row.type))
},
},
{
prop: 'status',
label: '发布状态',
width: 400,
render: (h, {row}) => {
return h('span', null, this.dict.getLabel('bannerSstatus', row.status))
},
},
{slot: 'option', label: '操作', width: 280},
],
}
},
// 计算
computed: {
...mapState(['user']),
},
// 监听
watch: {},
// 实例创建后
created() {
this.dict.load('bannerType', 'bannerSstatus').then(() => {
this.getList()
})
// this.getList()
this.getShopList()
},
// 实例渲染后
mounted() {
},
// 方法
methods: {
getList() {
this.instance
.post(`/app/appbanner/list`, null, {
params: {
...this.search,
...this.page,
},
})
.then((res) => {
if (res.code == 0) {
// console.log(res.data.records)
this.tableData = res.data.records
this.page.total = res.data.total
}
})
.catch(() => {
})
},
getShopList() {
},
add() {
this.dialog.title = '添加'
this.dialogInfo = {}
this.getList()
this.visible = true
},
data(e) {
this.files.push(e)
},
change(e) {
this.files = e
},
// 确定新增
addConfirm() {
// console.log('确定')
this.$refs.ruleForm.validate((valid) => {
if (valid) {
this.instance.post(`/app/appbanner/addOrUpdate`, {
imgUrl: this.dialogInfo.imgUrl[0].url,
linkUrl: this.dialogInfo.linkUrl,
status: this.status,
title: this.dialogInfo.title,
type: this.dialogInfo.type,
id: this.dialogInfo.id,
}).then(res => {
if (res?.code == 0) {
this.$message.success('新增成功')
this.visible = false
this.getList()
}
})
}
})
},
// 发布/取消发布
release(row) {
this.$confirm('确定此操作?').then(() => {
var status = row.status == 1 ? '0' : '1'
this.instance.post(`/app/appbanner/setStatus?id=${row.id}&status=${status}`)
.then((res) => {
if (res?.code == 0) {
this.getList()
}
})
})
},
// 详情
detail(row) {
this.detailDialog = true
this.instance.post(`/app/appbanner/detail?id=${row.id}`, {
// imgUrl: this.dialogInfo.imgUrl[0].url
})
.then((res) => {
if (res?.data) {
this.dialogInfo = {...row}
}
})
},
// 编辑
edit(row) {
this.dialog.title = '编辑'
this.visible = true
this.dialogInfo = {...row}
this.dialogInfo.imgUrl = [{url: ''}]
this.dialogInfo.imgUrl[0].url = row.imgUrl
// console.log(this.dialogInfo)
},
// 删除
remove(id) {
this.$confirm('删除后不可恢复,是否要删除该事项?', {
type: 'error',
}).then(() => {
this.instance.post(`/app/appbanner/delete?ids=${id}`).then((res) => {
if (res.code == 0) {
this.$message.success('删除成功!')
this.getList()
}
})
})
},
},
}
</script>
<style lang="scss">
.appcarousel {
height: 100%;
// padding: 15px 10px;
.banner-img {
width: 50px;
height: 50px;
}
}
</style>

View File

@@ -0,0 +1,71 @@
<template>
<component
v-if="hasApp && configLoaded"
:is="componentName"
:params="params"
:backType="backType"
:instance="instance"
:dict="dict"
:appType="appType"
:appId="appId"
:configs="configs"
@change="changePage" />
<ai-empty v-else-if="hasApp">应用配置加载中...</ai-empty>
<ai-empty v-else>读取应用失败</ai-empty>
</template>
<script>
import Add from './components/Add.vue'
import Detail from './components/Detail.vue'
import List from './components/List.vue'
export default {
label: '代码生成',
name: 'AppCodeGeneration',
props: {
instance: Function,
dict: Object
},
components: {Add, Detail, List},
computed: {
appId() {
return this.$route.query.app
},
hasApp() {
return !!this.appId
},
configLoaded() {
return !!this.configs.id
}
},
data() {
return {
componentName: 'list',
params: {},
backType: '',
configs: {},
appType: ''
}
},
methods: {
changePage(data) {
this.componentName = data.type
this.params = data.params
this.backType = data.backType || ''
},
getConfigs() {
this.instance.post(`/app/appapplicationinfo/queryApplicationInfo`, null, {
params: {appId: this.appId}
}).then((res) => {
if (res?.data) {
this.appType = res.data.appType
this.configs = res.data
}
})
}
},
created() {
this.getConfigs()
}
}
</script>

View File

@@ -0,0 +1,230 @@
<template>
<ai-card title="走访记录" class="visit" v-loading="loading">
<el-button slot="right" icon="iconfont iconAdd" type="text" @click="isShow = true">添加走访记录</el-button>
<template #content>
<div class="visit-list">
<div class="visit-item" v-for="(item, index) in list" :key="index">
<div class="visit-item__top">
<div class="left">
<div class="avatar">{{ item.name.substr(item.name.length - 2) }}</div>
<h2>{{ item.name }}</h2>
</div>
<span>{{ item.visitTime }}</span>
</div>
<b>{{ item.title }}</b>
<p>{{ item.description }}</p>
<div class="visit-imgs" v-if="item.images.length">
<ai-uploader v-model="item.images" :instance="instance" :limit="9" disabled/>
</div>
<div class="visit-status">
<span>现实状态</span>
<i>{{ dict.getLabel('visitCondolenceReality', item.reality) }}</i>
</div>
</div>
<ai-empty v-if="!list.length"></ai-empty>
</div>
<ai-dialog
:visible.sync="isShow"
width="1000px"
height="500px"
title="添加走访记录"
@close="onClose"
@onConfirm="onConfirm">
<el-form ref="form" :model="form" label-width="110px" label-position="right" size="small">
<ai-bar title="走访记录"></ai-bar>
<div class="ai-form" :model="form" label-width="110px" label-position="right">
<el-form-item label="走访时间" prop="visitTime"
:rules="[{ required: true, message: '请选择走访时间', trigger: 'change' }]">
<el-date-picker
v-model="form.visitTime"
type="datetime"
style="width: 100%;"
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="请选择走访时间">
</el-date-picker>
</el-form-item>
<el-form-item label="现实状态" prop="reality"
:rules="[{ required: true, message: '请选择现实状态', trigger: 'change' }]">
<ai-select
v-model="form.reality"
:selectList="dict.getDict('visitCondolenceReality')"
laceholder="请选择现实状态">
</ai-select>
</el-form-item>
<el-form-item label="入户走访事项" prop="title" style="width: 100%;"
:rules="[{ required: true, message: '请输入 入户走访事项'}]">
<el-input placeholder="请输入 入户走访事项" v-model="form.title" maxlength="30" show-word-limit/>
</el-form-item>
<el-form-item label="入户走访内容" prop="description" style="width: 100%;">
<el-input type="textarea" placeholder="请输入 入户走访内容" v-model="form.description" :rows="4" maxlength="500"
show-word-limit/>
</el-form-item>
<el-form-item label="图片" prop="images" style="width: 100%;">
<ai-uploader v-model="form.images" :instance="instance" :limit="9" isShowTip/>
</el-form-item>
</div>
</el-form>
</ai-dialog>
</template>
</ai-card>
</template>
<script>
import {mapState} from 'vuex'
export default {
name: 'visit',
props: ['id', 'dict', 'instance', 'appId', 'name', 'areaId'],
data() {
return {
appList: [],
list: [],
loading: false,
isShow: false,
form: {
visitTime: '',
reality: '',
images: [],
description: ''
}
}
},
computed: {
...mapState(['user'])
},
mounted() {
this.dict.load('visitCondolenceReality').then(() => {
this.getList()
})
},
methods: {
getList() {
this.loading = true
this.instance.post(`/app/appvisitvondolence/list?optionId=${this.id}&size=10000`).then(res => {
if (res.code === 0) {
this.list = res.data.records.map(item => {
return {
...item,
images: item.images ? JSON.parse(item.images) : []
}
})
}
this.loading = false
})
},
onConfirm() {
this.$refs.form.validate((valid) => {
if (valid) {
this.instance.post(`/app/appvisitvondolence/addOrUpdate`, {
...this.form,
optionId: this.id,
images: JSON.stringify(this.form.images),
applicationId: this.appId,
name: this.name,
areaId: this.areaId
}).then(res => {
if (res.code === 0) {
this.$message.success('添加成功')
this.isShow = false
this.getList()
}
})
}
})
},
onClose() {
this.form = {}
}
}
}
</script>
<style lang="scss" scoped>
.visit {
.visit-list {
.visit-item {
padding: 10px 0;
border-bottom: 1px solid #eee;
&:first-child {
padding-top: 0;
}
&:last-child {
border-bottom: none;
}
.visit-status {
display: flex;
align-items: center;
font-size: 14px;
span {
color: #333;
}
i {
color: #999;
font-style: normal;
}
}
& > p {
line-height: 1.4;
margin-bottom: 4px;
text-align: justify;
color: #666;
font-size: 16px;
}
b {
display: block;
margin-bottom: 6px;
}
.visit-item__top {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
span {
font-size: 14px;
color: #999;
}
.left {
display: flex;
align-items: center;
img, .avatar {
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
margin-right: 10px;
border-radius: 50%;
font-size: 14px;
color: #fff;
background: #26f;
}
h2 {
font-size: 16px;
font-weight: 500;
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,533 @@
<template>
<ai-detail v-if="pageShow" class="add-form">
<template #title>
<ai-title :title="params.id ? '编辑'+colData.applicationName : '新增' + colData.applicationName" isShowBottomBorder
isShowBack @onBackClick="onBack(true)"></ai-title>
</template>
<template #content>
<el-form ref="formData" class="ai-form" :rules="rules" :model="formData" label-width="110px" size="small">
<ai-card :title="items[0].groupName" v-for="(items, indexs) in formDataList" :key="indexs" v-if="items.length">
<template slot="content">
<div v-for="(item, index) in items" :key="index" :style="item.grid == 1 ? 'width: 100%;' : 'width: 50%;'"
class="form-div">
<el-form-item :label="item.fieldName" :prop="item.fieldDbName" style="width: 100%">
<!-- 字典下拉选择 -->
<template v-if="item.type == 'dict'">
<ai-select v-model="formData[item.fieldDbName]" :placeholder="item.fieldName" :selectList="dict.getDict(item.dict)" :disabled="item.disable == 1 || !!(formData.resident_id && item.isInit)" />
</template>
<!-- 单选radio -->
<template v-else-if="item.type == 'radio'">
<el-radio-group v-model="formData[item.fieldDbName]" :disabled="item.disable == 1 || !!(formData.resident_id && item.isInit)">
<el-radio :label="item.dictValue" v-for="(item, i) in dict.getDict(item.dict)" :key="i">{{ item.dictName }}</el-radio>
</el-radio-group>
</template>
<!-- 开关onOff -->
<template v-else-if="item.type == 'onOff'">
<el-switch v-model="formData[item.fieldDbName]" active-color="#26f" inactive-color="#ddd" active-value="1" inactive-value="0" :disabled="item.disable == 1 || !!(formData.resident_id && item.isInit)"></el-switch>
</template>
<!-- 多选checkbox -->
<template v-else-if="item.type == 'checkbox'">
<el-checkbox-group v-model="formData[item.fieldDbName]" :disabled="item.disable == 1 || !!(formData.resident_id && item.isInit)">
<el-checkbox v-for="(item, i) in dict.getDict(item.dict)" :label="item.dictValue" :key="i">
{{ item.dictName }}
</el-checkbox>
</el-checkbox-group>
</template>
<!-- 网格 -->
<template v-else-if="item.type === 'gird'">
<el-input disabled :value="girdName" size="small" placeholder="请选择网格">
<template slot="append">
<el-button size="small" @click="showGrid = true, treeObj.checkedKeys = formData[item.fieldDbName] ? [formData[item.fieldDbName]] : [], gridFieldName = item.fieldDbName">选择网格</el-button>
</template>
</el-input>
</template>
<template v-else-if="item.type === 'resident'">
<el-input
v-model="formData.name"
:placeholder="'请选择'+item.fieldName">
<template slot="append">
<ai-person-select
:instance="instance"
:disabled="!!params.id"
:url="'/app/appresident/list?auditType=1&areaId=' + user.info.areaId"
:isMultiple="false" dialogTitle="选择" @selectPerson="onChange">
<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>
</template>
</el-input>
</template>
<template v-else-if="item.type == 'idNumber'">
<ai-id v-model="formData[item.fieldDbName]" :disabled="item.disable == 1 || !!(formData.resident_id && item.isInit)" />
</template>
<!-- input输入框 -->
<template v-else-if="item.type == 'input' || item.type == 'name' || item.type == 'phone'">
<el-input v-model="formData[item.fieldDbName]" :placeholder="'请输入'+item.fieldName" clearable :disabled="item.disable == 1 || !!(formData.resident_id && item.isInit)"
:maxlength="item.maxLength" show-word-limit></el-input>
</template>
<!-- number 输入框 -->
<template v-else-if="item.type == 'number'">
<el-input-number v-model="formData[item.fieldDbName]" :label="'请输入'+item.fieldName" :disabled="item.disable == 1 || !!(formData.resident_id && item.isInit)" :precision="item.decimalPlaces" :max="item.maxValue" :min="item.minValue"></el-input-number>
</template>
<!-- textarea输入框 -->
<template v-else-if="item.type == 'textarea' || item.type == 'text'">
<el-input v-model="formData[item.fieldDbName]" :placeholder="'请输入'+item.fieldName" clearable :disabled="item.disable == 1 || !!(formData.resident_id && item.isInit)"
:maxlength="item.maxLength" type="textarea" show-word-limit :rows="3"></el-input>
</template>
<!-- 日期选择 -->
<template v-else-if="item.type == 'date'">
<el-date-picker style="width: 100%;" v-model="formData[item.fieldDbName]" type="date" placeholder="请选择" :disabled="item.disable == 1 || !!(formData.resident_id && item.isInit)"
:value-format="item.timePattern"></el-date-picker>
</template>
<!-- 日期带时分秒选择 -->
<template v-else-if="item.type == 'datetime'">
<el-date-picker v-model="formData[item.fieldDbName]" type="datetime" placeholder="选择日期时间" :disabled="item.disable == 1 || !!(formData.resident_id && item.isInit)"
:value-format="item.timePattern"></el-date-picker>
</template>
<!-- 时间-时分秒选择 -->
<template v-else-if="item.type == 'time'">
<el-time-picker v-model="formData[item.fieldDbName]" placeholder="请选择" :disabled="item.disable == 1 || !!(formData.resident_id && item.isInit)"
:value-format="item.timePattern"></el-time-picker>
</template>
<!-- 附件 -->
<template v-else-if="item.type == 'upload'">
<ai-uploader :instance="instance" isShowTip fileType="file" v-model="formData[item.fieldDbName]" :disabled="item.disable == 1"
acceptType=".zip,.rar,.doc,.docx,.xls,.ppt,.pptx,.pdf,.txt,.jpg,.png,.xlsx"
:limit="item.fileMaxCount" :maxSize="item.fileChoseSize"></ai-uploader>
</template>
<!-- 富文本 -->
<template v-else-if="item.type == 'rtf'">
<ai-editor v-model="formData[item.fieldDbName]" :instance="instance" :disabled="item.disable == 1 || !!(formData.resident_id && item.isInit)"/>
</template>
<!-- 地区选择 -->
<template v-else-if="item.type == 'area'">
<ai-area-get :instance="instance" v-model="formData[item.fieldDbName]" :name.sync="formData[item.fieldDbName +'_name']" :disabled="item.disable == 1 || !!(formData.resident_id && item.isInit)"/>
</template>
<!-- 人员选择 -->
<div class="especial" v-else-if="item.type == 'user'">
<span class="icon">&nbsp;</span>
<span class="people">{{ item.fieldName }}:</span>
<ai-wechat-selecter slot="append" isShowUser :instance="instance"
v-model="formData[item.fieldDbName]"></ai-wechat-selecter>
</div>
</el-form-item>
</div>
</template>
</ai-card>
</el-form>
<ai-dialog
title="选择网格"
:visible.sync="showGrid"
:destroyOnClose="true"
@close="showGrid = false"
@onConfirm="getCheckedTree"
width="720px">
<div class="grid">
<el-tree
:data="treeObj.treeList"
:props="treeObj.defaultProps"
node-key="id"
ref="tree"
:check-strictly="true"
show-checkbox
:default-checked-keys="treeObj.checkedKeys"
default-expand-all
@check="onCheckChange">
</el-tree>
</div>
</ai-dialog>
</template>
<template #footer>
<el-button class="delete-btn footer-btn" @click="onBack">取消</el-button>
<el-button class="footer-btn" type="primary" :loading="isLoading" @click="submit('formData')">提交</el-button>
</template>
</ai-detail>
</template>
<script>
import {mapState} from 'vuex'
export default {
name: 'Add',
props: {
instance: Function,
params: Object,
dict: Object,
appId: String,
backType: String,
configs: Object
},
data() {
return {
formData: {
age: 2
},
pickerOptions0: {
disabledDate(time) {
return time.getTime() < Date.now() - 8.64e7;
}
},
isLoading: false,
girdName: '',
gridFieldName: '',
showGrid: false,
formDataList: [],
pageShow: true,
treeObj: {
treeList: [],
defaultProps: {
children: "girdList",
label: "girdName",
},
checkedKeys: [],
},
colData: {},
areaId: ''
}
},
computed: {
...mapState(['user']),
rules() {
let rules = {}
this.colData?.tableInfos?.map(e => {
rules[e.fieldDbName] = []
if (e.mustFill == 1) {//是否必填
rules[e.fieldDbName]?.push({required: true, message: e.fieldTips})
}
})
return rules
}
},
created() {
this.getGridList()
this.getFormData()
if (this.params.id) {
this.getDetail()
}
},
methods: {
getGridList() {
this.instance.post(`/app/appgirdinfo/listAll`).then((res) => {
if (res.code == 0) {
this.treeObj.treeList = this.format(res.data)
if (this.formData.girdId) {
this.$set(this.treeObj, 'checkedKeys', [this.formData.girdId])
}
}
})
},
onChange (e) {
this.formData.resident_id = e.id
Object.keys(this.formData).forEach(item => {
for (var p in e){
if (item === p) {
this.$set(this.formData, item, e[item])
}
}
})
const idNumberInfo = this.idCardNoUtil.getIdCardInfo(e.idNumber)
this.$set(this.formData, 'birthDate', idNumberInfo.birthday)
this.formData.photo = e.photo ? [{
url: e.photo
}] : []
},
getCheckedTree() {
const gird = this.$refs.tree.getCheckedNodes()
if (gird.length) {
this.girdName = gird[0].girdName
this.formData[this.gridFieldName] = gird[0].id
} else {
this.girdName = ''
this.formData[this.gridFieldName] = ''
}
this.showGrid = false
},
onCheckChange (e) {
this.$nextTick(() => {
this.$refs.tree.getCheckedKeys().forEach(v => {
this.$refs.tree.setChecked(v, false)
})
this.$refs.tree.setChecked(e.id, true)
})
},
format (list) {
return list.map(item => {
if (item.girdLevel !== '2') {
item.disabled = true
}
if (item.girdList && item.girdList.length) {
item.girdList = this.format(item.girdList)
}
return item
})
},
initForm(data) {
this.colData = data
let dictList = []
let formList = {}
data.tableInfos.map((item) => {
let colItem
if (item.dictionaryCode) {
dictList.push(item.dictionaryCode)
}
if (item.dictionaryCode && item.type != 'radio' && item.type != 'checkbox' && item.type != 'onOff') {
colItem = {
...item,
type: 'dict',
dict: item.dictionaryCode
}
if (!this.params.id) {
colItem.fieldValue = item.defaultValue || ''
}
} else if (item.type == 'radio') {
colItem = {
...item,
dict: item.dictionaryCode,
}
if (!this.params.id) {
colItem.fieldValue = item.defaultValue || ''
}
} else if (item.type == 'checkbox') {
colItem = {
...item,
dict: item.dictionaryCode,
fieldValue: []
}
if (!this.params.id && item.defaultValue) {
var val = item.defaultValue?.split('`')
colItem.fieldValue = val
}
} else if (item.type === 'upload') {
colItem = {
...item,
fieldValue: []
}
} else if (item.type == 'onOff') {
colItem = {
...item,
}
if (!this.params.id) {
colItem.fieldValue = item.defaultValue || 0
}
} else if (item.type == 'number') {
colItem = {
...item,
type: item.type,
min: item.minValue || 1,
max: item.maxValue || 1000000000,
minValue: item.minValue || 1,
maxValue: item.maxValue || 1000000000
}
if (!this.params.id) {
colItem.fieldValue = Number(item.defaultValue) || 0
}
}else {
if (item.type == 'date' && !item.timePattern) {
item.timePattern = 'yyyy-MM-dd'
}
if (item.type == 'datetime' && !item.timePattern) {
item.timePattern = 'yyyy-MM-dd HH:mm:ss'
}
if (item.type == 'time' && !item.timePattern) {
item.timePattern = 'HH:mm:ss'
}
colItem = {
...item,
type: item.type,
}
if (!this.params.id) {
colItem.fieldValue = item.defaultValue || ''
}
}
// console.log(colItem)
formList[item.groupIndex]?.push(colItem) || (formList[item.groupIndex] = [colItem])
if (item.type === 'number') {
this.$set(this.formData, item.fieldDbName, 1)
} else {
this.$set(this.formData, item.fieldDbName, colItem.fieldValue || '')
}
})
this.formDataList = Object.values(formList)
this.$forceUpdate()
if (dictList.length) {
this.getDictList(dictList)
} else {
this.pageShow = true
}
},
getFormData() {
this.initForm(this.configs)
},
getDetail() {
this.instance.post(`/app/appapplicationinfo/queryDetailById?appId=${this.appId}&id=${this.params.id}`).then((res) => {
if (res?.data) {
this.configs.tableInfos.map((item) => {
this.formData[item.fieldDbName] = res.data[item.fieldDbName] || ''
if(item.type == 'checkbox') {
var checkList = this.formData[item.fieldDbName]?.split(',')
this.formData[item.fieldDbName] = checkList
}
if (item.type === 'gird' && this.formData[item.fieldDbName]) {
this.girdName = res.data[`${item.fieldDbName}_name`]
}
if (item.type === 'upload' && !this.formData[item.fieldDbName]) {
this.formData[item.fieldDbName] = []
}
if (item.type === 'upload' && this.formData[item.fieldDbName]) {
this.formData[item.fieldDbName] = this.formData[item.fieldDbName].split(',').map(v => {
return {
url: v
}
})
}
})
}
})
},
getDictList(listName) {
this.dict.load(listName.join(',')).then(() => {
this.pageShow = true
})
},
submit() {
this.$refs.formData?.validate((valid) => {
if (valid) {
this.isLoading = true
this.formDataList.map((item) => {
if (item.length) {
item.map((items) => {
if (items.type == 'checkbox' && this.formData[items.fieldDbName].length) { //多选
this.formData[items.fieldDbName] = this.formData[items.fieldDbName]?.toString()
}
if (items.type == 'upload') {
this.formData[items.fieldDbName] = this.formData[items.fieldDbName].map(v => v.url).join(',')
}
if(items.type == 'gird' && this.formData[items.fieldDbName]) {
this.formData[items.fieldDbName] = this.formData[items.fieldDbName] + '_' + this.girdName
}
if(items.type == 'area' && this.formData[items.fieldDbName]) {
var area = []
area.push(this.formData[items.fieldDbName])
area.push(this.formData[items.fieldDbName+'_name'])
this.formData[items.fieldDbName] = area.join('_')
}
})
}
})
this.instance.post(`/app/appapplicationinfo/addOrUpdate?appId=${this.appId}`, {
...this.formData,
id: this.params.id || ''
}).then((res) => {
this.isLoading = false
if (res.code == 0) {
this.$message.success('提交成功')
setTimeout(() => {
this.onBack(true)
}, 600)
}
}).catch(() => {
this.isLoading = false
})
}
})
},
onBack(isRefresh) {
this.$emit('change', {
type: this.backType == 'Detail' ? 'detail' : 'list',
params: this.params,
isRefresh: !!isRefresh,
})
},
},
}
</script>
<style scoped lang="scss">
::v-deep .ai-card{
width: 100%;
}
.form-div {
display: inline-block;
vertical-align: top;
}
.add-form {
::v-deep .AiPersonSelect {
.el-button {
border-color: transparent;
background-color: transparent;
color: inherit;
border-top: 0;
border-bottom: 0;
background: transparent;
}
}
}
.especial {
margin-bottom: 12px;
.icon {
vertical-align: top;
display: inline-block;
padding-top: 5px;
margin-left: 20px;
color: #f46;
}
.people {
display: inline-block;
font-size: 14px;
color: #666;
padding-right: 12px;
vertical-align: top;
width: 64px;
word-break: break-all;
box-sizing: border-box;
}
.AiWechatSelecter {
display: inline-block;
margin-left: 3px;
}
.hint {
font-size: 14px;
color: #999;
margin-left: 16px;
}
.mar-r40 {
margin-right: 40px;
}
.w80 {
width: 80px;
text-align: right;
color: #888;
}
}
.AiWechatSelecter {
width: calc(100% - 120px);
}
</style>

View File

@@ -0,0 +1,257 @@
<template>
<ai-detail v-loading="pageShow" isHasSidebar>
<template #title>
<ai-title :title="colData.applicationName+'详情'" isShowBottomBorder isShowBack @onBackClick="onBack(true)"></ai-title>
</template>
<template #content>
<AiSidebar :tabTitle="tabTitle" v-model="currIndex" v-if="appType"></AiSidebar>
<ai-card v-show="currIndex === 0" :title="items[0].groupName" v-for="(items, indexs) in formDataList" :key="indexs">
<template slot="content">
<ai-wrapper>
<ai-info-item :label="item.fieldName" v-show="item.type !== 'resident'" v-for="(item, index) in items" :key="index" :isLine="item.grid == 1">
<span v-if="item.dict && item.type != 'checkbox'">{{$dict.getLabel(item.dict, formData[item.fieldDbName]) || '-'}}</span>
<span v-else-if="item.type == 'checkbox'">{{formData[item.fieldDbName]}}</span>
<span v-else-if="item.type == 'rtf'" v-html="formData[item.fieldDbName]"></span>
<ai-file-list :fileList="formData[item.fieldDbName]" v-else-if="item.type == 'upload'" :fileOps="{name: 'name', size: 'fileSizeStr'}"></ai-file-list>
<span v-else-if="item.type == 'area'">{{ formData[item.fieldDbName + '_name'] }}</span>
<span v-else-if="item.type == 'gird'">{{ formData[item.fieldDbName + '_name'] }}</span>
<span v-else>{{ formData[item.fieldDbName] || '-' }}</span>
</ai-info-item>
</ai-wrapper>
</template>
</ai-card>
<component
:is="component"
:name="params.name || params.name00"
:areaId="formData.area"
:id="params.id"
:appId="appId"
:dict="dict"
:instance="instance"
v-if="currIndex === 1">
</component>
</template>
</ai-detail>
</template>
<script>
import { mapState } from 'vuex'
import Visit from '../app/visit/Visit'
export default {
name: 'Detail',
props: {
dict: Object,
appId: String,
params: Object,
configs: Object,
appType: String,
instance: Function
},
components: {
Visit
},
data () {
return {
formData: {},
colData: {},
formDataList: [],
tabTitle: ['人员信息'],
pageShow: false,
currIndex: 0,
component: ''
}
},
computed: {
...mapState(['user']),
},
created () {
if (!this.appType) return
if (this.appType === '0') {
this.component = 'Visit'
}
this.dict.load('diyAppType').then(() => {
this.tabTitle.push(this.dict.getLabel('diyAppType', this.appType))
})
},
mounted () {
this.pageShow = true
this.getDetail()
},
methods: {
initForm (data) {
this.colData = data
let dictList = []
let formList = {}
data.tableInfos.map((item) => {
let colItem
if (item.dictionaryCode) {
dictList.push(item.dictionaryCode)
}
if (item.dictionaryCode && item.type != 'radio' && item.type != 'checkbox') {
colItem = {
...item,
type: 'dict',
dict: item.dictionaryCode
}
} else if (item.type == 'radio') {
colItem = {
...item,
dict: item.dictionaryCode,
}
} else if (item.type == 'checkbox') {
colItem = {
...item,
dict: item.dictionaryCode,
fieldValue: item.fieldValue?.split(',') || []
}
} else if (item.type == 'onOff') {
colItem = {
...item,
fieldValue: 0
}
} else {
if (item.type == 'date' && !item.timePattern) {
item.timePattern = 'yyyy-MM-dd'
}
if (item.type == 'datetime' && !item.timePattern) {
item.timePattern = 'HH:mm:ss yyyy-MM-dd'
}
if (item.type == 'time' && !item.timePattern) {
item.timePattern = 'HH:mm:ss'
}
colItem = {
...item,
type: item.type,
}
}
formList[item.groupIndex]?.push(colItem) || (formList[item.groupIndex] = [colItem])
if (item.type === 'upload') {
this.$set(this.formData, colItem.fieldDbName, this.formData[colItem.fieldDbName] ? this.formData[colItem.fieldDbName].split(',').map(v => {
return {
url: v
}
}) : [])
} else {
this.$set(this.formData, colItem.fieldDbName, this.formData[colItem.fieldDbName] || "")
}
})
this.formDataList = Object.values(formList)
this.$forceUpdate()
if (dictList.length) {
this.getDictList(dictList)
} else {
this.pageShow = true
}
},
getFormData () {
this.initForm(this.configs)
},
getDetail () {
this.instance.post(`/app/appapplicationinfo/queryDetailById?appId=${this.appId}&id=${this.params.id}`).then((res) => {
if (res?.data) {
this.formData = res.data
this.getFormData()
this.pageShow = false
}
}).catch(() => {
this.pageShow = false
})
},
getDictList (listName) {
this.dict.load(listName.join(',')).then(() => {
})
},
onBack (isRefresh) {
this.$emit('change', {
type: 'list',
isRefresh: !!isRefresh,
})
},
toEdit () {
this.$emit('change', {
type: 'Add',
params: this.params,
backType: 'Detail'
})
}
}
}
</script>
<style scoped lang="scss">
.form-div{
display: inline-block;
}
.especial {
margin-bottom: 12px;
.icon {
vertical-align: top;
display: inline-block;
padding-top: 5px;
margin-left: 20px;
color: #f46;
}
.people {
display: inline-block;
font-size: 14px;
color: #666;
margin-right: 16px;
vertical-align: top;
}
.AiWechatSelecter {
display: inline-block;
margin-left: 3px;
}
.hint {
font-size: 14px;
color: #999;
margin-left: 16px;
}
.mar-r40{
margin-right: 40px;
}
.w80{
width: 80px;
text-align: right;
color: #888;
}
}
.add-icon{
text-align: right;
cursor: pointer;
i{
font-size: 14px;
}
}
.color1{
color:#4B87FE;
}
.color2{
color:#2EA222;
}
.color3{
color:#999999;
}
.AiWechatSelecter{
width: calc(100% - 150px);
}
</style>

View File

@@ -0,0 +1,305 @@
<template>
<ai-list v-if="pageShow">
<template slot="title">
<ai-title :title="configs.applicationName" isShowBottomBorder></ai-title>
</template>
<template slot="content">
<ai-search-bar v-if="searchList.length" bottomBorder style="margin-bottom: 12px;">
<template #left>
<div v-for="(item, index) in searchList" :key="index">
<ai-select
v-model="search[item.searchValue]"
:placeholder="'请选择'+item.label" clearable
@change="$forceUpdate();(page.current = 1), getList()"
:selectList="dict.getDict(item.dict)"
v-if="item.type == 'dict'">
</ai-select>
<ai-search v-else-if="item.type == 'date'" :label="item.label">
<el-date-picker size="small" v-model="search[item.searchValue]" type="daterange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期"
@change="$forceUpdate();(page.current = 1), getList()"
value-format="yyyy-MM-dd"/>
</ai-search>
<ai-search v-else-if="item.type == 'time'" :label="item.label">
<el-time-picker is-range size="small" v-model="search[item.searchValue]" range-separator="至"
start-placeholder="开始时间" end-placeholder="结束时间" placeholder="选择时间范围"
@change="$forceUpdate();(page.current = 1), getList()"
value-format="HH:mm:ss"></el-time-picker>
</ai-search>
<ai-search v-else-if="item.type == 'area'" :label="item.label">
<ai-area-get :instance="instance" v-model="search[item.searchValue]"/>
</ai-search>
</div>
</template>
<template #right v-if="showRightInput">
<el-input :placeholder="placeholder" v-model="search.searchParam" size="small"
@keyup.enter.native="$forceUpdate();(page.current = 1), getList()"
@clear="$forceUpdate();(page.current = 1), getList()"
@change="$forceUpdate();(page.current = 1), getList()" clearable prefix-icon="iconfont iconSearch"/>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<el-button type="primary" icon="iconfont iconAdd" size="small"
v-if="configs.insertEnable == 1" @click="toAdd('', 'Add')">添加
</el-button>
<el-button icon="iconfont iconDelete" size="small" :disabled="ids.length == 0"
v-if="configs.batchDelEnable == 1" @click="delAll()">删除
</el-button>
</template>
<template #right>
<ai-import :instance="instance" v-if="configs.importEnable == 1" type="appapplicationinfo"
:importParams="{appId}" :tplParams="{appId}" :name="configs.applicationName" @success="getList()">
<el-button icon="iconfont iconImport">导入</el-button>
</ai-import>
<el-button icon="iconfont iconExported" size="small" v-if="configs.exportEnalbe == 1" @click="down()"
>导出
</el-button>
</template>
</ai-search-bar>
<ai-table class="ai-table" :tableData="tableData" :col-configs="colConfigs" :total="page.total"
:current.sync="page.current" :size.sync="page.size" @getList="getList" :dict="dict"
@selection-change="(v) => (ids = v.map((e) => e.id))">
<el-table-column v-for="(item, indexs) in colConfigs" :key="indexs" :slot="item.slot" :label="item.label"
show-overflow-tooltip
align="center">
<template slot-scope="{ row }">
<div v-if="item.type != 'checkbox' && item.dict">
{{ $dict.getLabel(item.dict, row[item.fieldDbName]) || '-' }}
</div>
<div v-if="item.type == 'rtf'" v-html="row[item.fieldDbName]"></div>
<div v-if="item.type == 'checkbox'">{{ row[item.fieldDbName] }}</div>
<div v-if="item.type == 'gird'">{{ row[item.fieldDbName + '_name'] }}</div>
</template>
</el-table-column>
<el-table-column slot="options" label="操作" fixed="right" align="center" width="160">
<template slot-scope="{ row }">
<div class="table-options">
<el-button type="text" @click="toDetail(row, 'Detail')">详情</el-button>
<el-button type="text" @click="toDetail(row, 'Add')" v-if="configs.editEnable == 1">编辑</el-button>
<el-button type="text" @click="del(row.id)" v-if="configs.deleteEnable == 1">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
</template>
</ai-list>
</template>
<script>
export default {
name: 'List',
props: {
instance: Function,
dict: Object,
params: Object,
appId: String,
configs: Object
},
data() {
return {
pageShow: false,
tableData: [],
colConfigs: [],
page: {
size: 10,
current: 1,
total: 0,
},
search: {
searchParam: '',
},
searchList: [],
ids: [],
showRightInput: false,
placeholder: '请输入',
}
},
created() {
this.initConfigs()
},
methods: {
initConfigs() {
var dictList = []
var colList = []
var searchList = []
var placeholderList = []
this.configs.showListFields.map((item) => {
var colItem
if (item.dictionaryCode) {
dictList.push(item.dictionaryCode)
colItem = {
slot: item.fieldDbName,
label: item.fieldName,
dict: item.dictionaryCode,
fieldDbName: item.fieldDbName,
type: item.type
}
} else if (item.type == 'rtf') {
colItem = {label: item.fieldName, type: item.type, slot: item.fieldDbName, fieldDbName: item.fieldDbName}
} else if (item.type == 'area') {
colItem = {prop: item.fieldDbName + '_name', label: item.fieldName, align: "center"}
} else {
colItem = {prop: item.fieldDbName, label: item.fieldName, align: "center"}
}
colList.push(colItem)
})
this.configs.fuzzyQueryFields.map((item) => {
var searchItem = {}
if (item.dictionaryCode) {
searchItem = {
type: 'dict',
label: item.fieldName,
dict: item.dictionaryCode,
searchValue: item.fieldDbName
}
}
if (item.type == 'input' || item.type == 'name' || item.type == 'idNumber' || item.type == 'phone') {
placeholderList.push(item.fieldName)
this.showRightInput = true
}
if (item.type == 'date') {
searchItem = {
type: 'date',
label: item.fieldName,
searchValue: item.fieldDbName
}
}
if (item.type == 'time') {
searchItem = {
type: 'time',
label: item.fieldName,
searchValue: item.fieldDbName
}
}
if (item.type == 'area') {
searchItem = {
type: 'area',
label: item.fieldName,
searchValue: item.fieldDbName
}
}
this.$set(this.search, item.fieldDbName, '')
searchList.push(searchItem)
})
this.colConfigs = colList
this.searchList = searchList
this.$forceUpdate()
var text = placeholderList.join('/')
this.placeholder = this.placeholder + text
if (this.configs.batchDelEnable == 1) {
this.colConfigs.unshift({type: 'selection', width: 100})
}
if (dictList.length) {
this.getDictList(dictList)
} else {
this.pageShow = true
this.getList()
}
},
getDictList(listName) {
this.dict.load(listName.join(',')).then(() => {
this.pageShow = true
this.getList()
})
},
getList() {
this.instance.post(`/app/appapplicationinfo/list?appId=${this.appId}&current=${this.page.current}&size=${this.page.size}`, {...this.search}).then((res) => {
if (res?.data) {
this.tableData = res.data.records
this.page.total = res.data.total
}
})
},
toDetail(item, type) {
this.$emit('change', {
type: type,
params: item,
backType: 'List'
})
},
toAdd() {
this.$emit('change', {
type: 'Add',
params: {
type: 'Add',
}
})
},
del(id) {
this.$confirm("删除后不可恢复是否要删除", {
type: 'error'
}).then(() => {
this.instance.post(`/app/appapplicationinfo/delete?appId=${this.appId}&ids=${id}`).then((res) => {
if (res.code == 0) {
this.$message.success("删除成功");
this.getList();
}
})
});
},
delAll() {
if (this.ids.length > 100) {
return this.$message.error("删除的数据最多不能超过100条");
}
var id = this.ids.join(',')
this.del(id)
},
reset() {
Object.keys(this.search).forEach((e) => {
this.search[e] = ''
})
this.getList()
},
down() {
var id = ''
if (this.ids.length) {
id = this.ids.join(',')
}
this.instance.post(`/app/appapplicationinfo/export?appId=${this.appId}&ids=${id}`, this.search, {
responseType: 'blob',
timeout: 100000
}).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', this.configs.applicationName + '.xls')
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
}).catch((err) => {
this.$error.success(err)
})
}
},
}
</script>
<style scoped lang="scss">
.mar-b10 {
margin-bottom: 10px;
}
</style>

View File

@@ -0,0 +1,164 @@
<template>
<section class="AppLicence">
<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="" />
<div class="content-right">
<h3 class="title">产品许可信息</h3>
<p class="mini-title">您当前的版本为Saas专业版非常感谢您对我们产品的认可与支持</p>
<div class="info">
<span class="label">过期时间</span>
<span class="value color-f46" v-if="info.isExpired == 1">{{info.expireDate ? info.expireDate+'(已过期)' : '未激活'}}</span>
<span class="value color-26f" v-else>{{info.expireDate}}</span>
</div>
<div class="info">
<span class="label">主板序列号</span>
<span class="value">{{info.mainBoard}}</span>
</div>
<div class="info">
<span class="label">CPU</span>
<span class="value">{{info.cpu}}</span>
</div>
<div class="info">
<span class="label">MAC地址</span>
<span class="value">{{info.mac}}</span>
</div>
<div class="info mar-b32">
<span class="label">IP地址</span>
<span class="value">{{info.ip}}</span>
</div>
<el-upload
class="upload-demo"
action
multiple
accept=".lic"
:http-request="uploadFile">
<div class="btn">上传许可</div>
</el-upload>
</div>
</div>
</template>
</ai-list>
</section>
</template>
<script>
export default {
name: "AppLicence",
label: "产品许可",
props: {
instance: Function,
dict: Object,
permissions: Function
},
data() {
return {
files: [],
info: {}
}
},
mounted() {
this.getDetail()
},
methods: {
getDetail() {
this.instance.post(`/admin/license/detail`).then(res => {
if (res?.data) {
this.info = res.data
}
})
},
uploadFile: function (file) {
let formData = new FormData();
formData.append("file", file.file);
this.instance.post(`/admin/license/save`, formData, {withoutToken: false}).then(res => {
if (res && res.code == 0) {
this.$message.success("证书上传成功!");
this.getDetail()
}
});
},
},
};
</script>
<style lang="scss" scoped>
.AppLicence {
width: 100%;
height: 100%;
::v-deep .ai-list{
background-color: #F5F6F9;
}
::v-deep .ai-list .ai-list__content--right .ai-list__content--right-wrapper{
background-color: #F5F6F9;
box-shadow: 0 0 0 0;
margin: 0!important;
padding: 0!important;
}
::v-deep .el-upload-list{
display: none;
}
.licence-content{
display: flex;
margin-top: 30px;
.left-img{
width: 200px;
height: 200px;
}
.content-right{
width: 800px;
.title{
font-size: 24px;
font-family: MicrosoftYaHei-Bold, MicrosoftYaHei;
font-weight: bold;
color: #222;
line-height: 24px;
margin-bottom: 8px;
}
.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;
color: #222;
.label{
width: 164px;
}
.value{
width: calc(100% - 164px);
}
.color-26f{
color: #26f;
}
.color-f46{
color: #f46;
}
}
.mar-b32{
margin-bottom: 32px;
}
.btn{
width: 88px;
height: 32px;
line-height: 32px;
text-align: center;
background: linear-gradient(90deg, #299FFF 0%, #0C61FF 100%);
border-radius: 2px;
cursor: pointer;
color: #fff;
font-size: 14px;
}
}
}
}
</style>

View File

@@ -0,0 +1,40 @@
<template>
<div class="AppShowProduce">
<List
slot="content"
:instance="instance"
:dict="dict"
:permissions="permissions">
</List>
</div>
</template>
<script>
import List from './components/List.vue'
export default {
name: 'AppNavConfig',
label: '导航配置',
components: {
List
},
props: {
instance: Function,
dict: Object,
permissions: Function
},
data () {
return {
}
}
}
</script>
<style lang="scss" scoped>
.AppShowProduce {
height: 100%;
}
</style>

View File

@@ -0,0 +1,515 @@
<template>
<ai-list class="list" v-loading="isLoading">
<ai-title slot="title" title="导航配置" isShowBottomBorder></ai-title>
<template slot="content">
<ai-card title="首页功能">
<template #right>
<el-button size="small" type="primary" @click="eidt">{{ isEdit ? '退出编辑' : '编辑' }}</el-button>
<el-button size="small" @click="save" v-if="isEdit">保存</el-button>
</template>
<div slot="content" class="item-wrapper">
<draggable
v-model="picked"
:animation="340"
group="select"
style="display: flex;"
handle=".mover">
<div class="item" :class="[isEdit ? 'mover' : '']" v-for="(item, i) in picked" :key="i">
<img :src="item.pictureUrl">
<i class="el-icon-remove icon" @click="removeChoose(i)" v-if="isEdit"></i>
<h2>{{ item.name }}</h2>
</div>
</draggable>
<ai-empty style="width: 100%" v-if="!picked.length"></ai-empty>
</div>
</ai-card>
<ai-card title="全部功能">
<template #right>
<el-button size="small" type="primary" @click="isShowAdd = true">新增应用</el-button>
<el-button size="small" type="danger" @click="isEdit = false, isRemove = !isRemove">{{ isRemove ? '取消删除' : '删除应用' }}</el-button>
</template>
<div class="all" slot="content">
<div class="item-row" v-for="(group, index) in list" :key="index">
<h2>{{ group.name }}</h2>
<div class="item-wrapper" v-if="isRest">
<div class="item" v-for="(item, i) in group.list" :key="i">
<img :src="item.pictureUrl">
<i class="el-icon-error icon" @click="removeApp(item.id)" v-if="isRemove && item.type !== '0' && item.picked !== '1'"></i>
<i class="el-icon-circle-plus icon" @click="addApp(item)" v-if="isCanAdd(item)"></i>
<h2>{{ item.name }}</h2>
<div class="item-setting">
<div class="item-mask"></div>
<div class="item-wrapper__icon" @click.stop="editApp(item)">
<i class="el-icon-setting"></i>
<div>编辑</div>
</div>
</div>
</div>
</div>
</div>
<ai-empty style="width: 100%" v-if="!list.length"></ai-empty>
</div>
</ai-card>
<ai-dialog
:visible.sync="isShowAdd"
width="780px"
height="580px"
:title="id ? '添加专题' : '编辑专题'"
@close="onClose"
@onConfirm="onConfirm">
<el-form ref="form" :model="form" label-width="110px" label-position="right">
<div class="ai-form" :model="form" label-width="110px" label-position="right">
<el-form-item label="应用名称" prop="name" style="width: 100%;" :rules="[{ required: true, message: '请输入应用名称', trigger: 'blur' }]">
<el-input size="small" :maxlength="8" placeholder="请输入应用名称" v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="应用模块" style="width: 100%;" prop="menuType" :rules="[{ required: true, message: '请选择应用模块', trigger: 'change' }]">
<ai-select
v-model="form.menuType"
:selectList="dict.getDict('homeConfigMenuType')"
@change="onChange"
laceholder="请选择应用模块">
</ai-select>
</el-form-item>
<el-form-item label="认证类型" style="width: 100%;" prop="checkType">
<ai-select
v-model="form.checkType"
:selectList="dict.getDict('miniConfigCheckType')"
laceholder="请选择认证类型">
</ai-select>
</el-form-item>
<el-form-item label="应用类型" style="width: 100%;" prop="type" :rules="[{ required: true, message: '请选择应用类型', trigger: 'change' }]">
<ai-select
v-model="form.type"
:selectList="typeDict"
@change="onChange"
laceholder="请选择应用类型">
</ai-select>
</el-form-item>
<el-form-item label="小程序appid" prop="appId" style="width: 100%;" v-if="form.type === '1'" :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 label="链接" prop="url" style="width: 100%;" v-if="form.type === '2'" :rules="[{ required: true, message: '请输入链接', trigger: 'blur' }]">
<el-input size="small" placeholder="请输入链接" v-model="form.url"></el-input>
</el-form-item>
<el-form-item label="小程序路径" prop="modulePath" style="width: 100%;" v-if="form.type === '3'" :rules="[{ required: true, message: '请输入小程序路径', trigger: 'blur' }]">
<el-input size="small" placeholder="请输入小程序路径" v-model="form.modulePath"></el-input>
</el-form-item>
<el-form-item label="客服链接" prop="url" style="width: 100%;" v-if="form.type === '4'" :rules="[{ required: true, message: '请输入客服链接地址', trigger: 'blur' }]">
<el-input size="small" placeholder="请输入客服链接地址" v-model="form.url"></el-input>
</el-form-item>
<el-form-item label="图标" prop="pictureUrl" style="width: 100%;" :rules="[{ required: true, message: '请上传图标 ', trigger: 'change' }]">
<ai-uploader v-model="form.pictureUrl" :instance="instance" :limit="1"></ai-uploader>
</el-form-item>
<!-- <el-form-item label="权限配置" v-if="form.type === '0'" prop="pictureUrl" style="width: 100%;">
<el-input disabled :value="girdName" size="small" placeholder="请选择地区">
<template slot="append">
<el-button size="small" @click="isShowArea = true">选择地区</el-button>
</template>
</el-input>
</el-form-item> -->
</div>
</el-form>
</ai-dialog>
<ai-dialog
title="选择权限"
:visible.sync="isShowArea"
:destroyOnClose="true"
@close="isShowArea = false"
@onConfirm="getCheckedTree"
width="720px">
<el-tree
:data="treeList"
:props="defaultProps"
node-key="id"
check-strictly
ref="tree"
show-checkbox
:default-checked-keys="defaultChecked"
:default-expanded-keys="defaultExpanded"
default-expand-all>
</el-tree>
</ai-dialog>
</template>
</ai-list>
</template>
<script>
import draggable from 'vuedraggable'
export default {
name: 'List',
props: {
instance: Function,
dict: Object
},
components: {
draggable
},
data () {
return {
picked: [],
id: '',
isEdit: false,
isRemove: false,
defaultExpanded: [],
isRest: true,
isShowArea: false,
isLoading: false,
defaultProps: {
children: "children",
label: "label",
},
treeList: [],
form: {
type: '',
pictureUrl: [],
url: '',
name: '',
checkType: '',
appId: '',
modulePath: ''
},
typeDict: [{
dictName: '外链小程序',
dictValue: '1'
}, {
dictName: '外链h5',
dictValue: '2'
}, {
dictName: '新闻',
dictValue: '3'
}, {
dictName: '客服',
dictValue: '4'
}],
info: {},
isShowAdd: false,
list: [],
defaultChecked: []
}
},
created () {
this.dict.load('homeConfigMenuType', 'miniConfigCheckType').then(() => {
this.getList()
})
},
methods: {
getList () {
this.instance.post(`/app/appminihomeconfig/listAll`).then(res => {
if (res.code == 0) {
this.picked = res.data.picked
this.list = Object.keys(res.data.all).map(item => {
return {
name: this.dict.getLabel('homeConfigMenuType', item),
list: res.data.all[item]
}
})
}
})
},
getCheckedTree () {
const keys = this.$refs.tree.getCheckedNodes(false, true).map(v => v.id)
this.instance.post(`/app/appminihomeconfig/addOrUpdate`, {
...this.info,
areaIds: keys
}).then(res => {
if (res.code === 0) {
this.$message.success('修改成功')
this.isShowArea = false
this.getList()
}
})
},
format (list) {
return list.map(item => {
if (item.girdLevel !== '2') {
item.disabled = true
}
if (item.girdList && item.girdList.length) {
item.girdList = this.format(item.girdList)
}
return item
})
},
editApp (e) {
if (e.type !== '0') {
this.form = {
...e,
pictureUrl: [{
url: e.pictureUrl
}]
}
this.isShowAdd = true
return false
}
this.info = e
this.isLoading = true
this.instance.post(`/app/appminihomeconfig/queryDetailById?id=${e.id}`).then(res => {
if (res.code === 0) {
let parent = res.data.areaConfigs.map(v => {
v.label = v.name
v.children = []
return v
}).filter(e => !e.parentId)[0]
this.defaultExpanded = res.data.areaConfigs.filter(v => v.checked).map(v => v.id)
this.defaultChecked = res.data.areaConfigs.filter(v => v.checked).map(v => v.id)
this.addChild(parent, res.data.areaConfigs)
this.treeList = [parent]
this.isShowArea = true
}
this.isLoading = false
}).catch(() => {
this.isLoading = false
})
},
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))
}
},
isCanAdd (item) {
const isHas = this.picked.map(v => v.id).indexOf(item.id) > -1
return this.isEdit && item.picked !== '1' && !isHas
},
addApp (e) {
if (this.picked.length >= 8) {
return this.$message.error('导航功能最多放置8个')
}
this.isRemove = false
this.picked.push(e)
},
eidt () {
if (this.isEdit) {
this.$confirm('确定退出编辑?').then(() => {
this.isEdit = false
this.getList()
})
return false
}
this.isEdit = !this.isEdit
},
removeChoose (index) {
this.picked.splice(index, 1)
this.isRest = false
this.$nextTick(() => {
this.isRest = true
})
},
save () {
this.instance.post(`/app/appminihomeconfig/updatePick?ids=${this.picked.map(v => v.id).join(',')}`).then(res => {
if (res.code === 0) {
this.$message.success('保存成功')
this.isEdit = false
this.isRemove = false
this.getList()
}
})
},
onClose () {
this.form.type = ''
this.form.pictureUrl = []
this.form.url = ''
this.form.name = ''
this.form.appId = ''
this.form.checkType = ''
},
onChange () {
this.form.url = ''
this.form.appId = ''
},
onConfirm () {
this.$refs.form.validate((valid) => {
if (valid) {
this.instance.post(`/app/appminihomeconfig/addOrUpdate`, {
...this.form,
pictureUrl: this.form.pictureUrl[0].url
}).then(res => {
if (res.code === 0) {
this.$message.success('添加成功')
this.isShowAdd = false
this.getList()
}
})
}
})
},
removeApp (id) {
this.$confirm('确定删除该数据?').then(() => {
this.instance.post(`/app/appminihomeconfig/delete?ids=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('删除成功!')
this.isRemove = false
this.getList()
}
})
})
},
remove (id) {
this.$confirm('确定删除该数据?').then(() => {
this.instance.post(`/app/appapplicationinfo/delete?ids=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('删除成功!')
this.getList()
}
})
})
}
}
}
</script>
<style scoped lang="scss">
.list {
::v-deep .ai-list__content {
padding: 0!important;
.ai-list__content--right-wrapper {
background: transparent!important;
box-shadow: none!important;
margin: 0!important;
padding: 12px 0 12px!important;
}
}
.all {
.item-row {
display: flex;
& > h2 {
margin-right: 40px;
font-weight: 500;
font-size: 16px;
}
}
}
.item-wrapper {
display: flex;
flex-wrap: wrap;
flex: 1;
margin-bottom: 20px;
.icon {
position: absolute;
top: 0;
right: 0;
z-index: 11;
font-size: 24px;
color: red;
transform: translate(50%, -20%);
&:hover {
opacity: 0.6;
cursor: pointer;
}
}
.item {
position: relative;
margin: 0 16px 16px 0;
text-align: center;
cursor: pointer;
.item-setting {
position: absolute;
top: 0;
left: 0;
z-index: 0;
opacity: 0;
width: 100%;
height: 100%;
transition: all ease 0.3s;
.item-wrapper__icon {
position: relative;
z-index: 1;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
div {
margin-top: 6px;
font-size: 16px;
}
}
i, div {
font-size: 16px;
color: #fff;
}
.item-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.6);
}
&:hover {
z-index: 1;
opacity: 1;
}
}
img {
width: 60px;
height: 60px;
border-radius: 50%;
}
h2 {
font-weight: normal;
font-size: 14px;
color: #666;
}
}
}
}
</style>

View File

@@ -0,0 +1,40 @@
<template>
<div class="AppThematicConfig">
<List
slot="content"
:instance="instance"
:dict="dict"
:permissions="permissions">
</List>
</div>
</template>
<script>
import List from './components/List.vue'
export default {
name: 'AppThematicConfig',
label: '专题配置',
components: {
List
},
props: {
instance: Function,
dict: Object,
permissions: Function
},
data () {
return {
}
}
}
</script>
<style lang="scss" scoped>
.AppThematicConfig {
height: 100%;
}
</style>

View File

@@ -0,0 +1,219 @@
<template>
<ai-list>
<ai-title slot="title" title="专题配置" isShowBottomBorder></ai-title>
<template slot="content">
<ai-search-bar bottomBorder>
<template #left>
<el-button type="primary" icon="iconfont iconAdd" size="small" @click="isShowAdd = true">添加 </el-button>
</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="img" width="160px" label="首页封面" align="center">
<template slot-scope="{ row }">
<ai-uploader v-model="row.img" disabled :instance="instance" :limit="1"></ai-uploader>
</template>
</el-table-column>
<el-table-column slot="options" width="160px" fixed="right" label="操作" align="center">
<template slot-scope="{ row }">
<div class="table-options" style="padding-right: 30px;">
<el-button type="text" @click="edit(row)">编辑</el-button>
<el-button type="text" @click="remove(row.id)">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
<ai-dialog
:visible.sync="isShowAdd"
width="680px"
height="580px"
:title="id ? '添加专题' : '编辑专题'"
@close="onClose"
@onConfirm="onConfirm">
<el-form ref="form" :model="form" label-width="110px" label-position="right">
<div class="ai-form" :model="form" label-width="110px" label-position="right">
<el-form-item label="专题名称" prop="name" style="width: 100%;" :rules="[{ required: true, message: '请输入专题名称', trigger: 'blur' }]">
<el-input size="small" placeholder="请输入专题名称" v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="首页封面" prop="pictureUrl" style="width: 100%;" :rules="[{ required: true, message: '请上传首页封面', trigger: 'change' }]">
<ai-uploader v-model="form.pictureUrl" :instance="instance" :limit="1"></ai-uploader>
</el-form-item>
<el-form-item label="应用类型" style="width: 100%;" prop="type" :rules="[{ required: true, message: '请选择应用类型', trigger: 'change' }]">
<ai-select
v-model="form.type"
:selectList="typeDict"
@change="onChange"
laceholder="请选择应用类型">
</ai-select>
</el-form-item>
<el-form-item label="小程序appid" prop="appId" style="width: 100%;" v-if="form.type === '0'" :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 label="链接" prop="url" style="width: 100%;" v-if="form.type === '1'" :rules="[{ required: true, message: '请输入链接', trigger: 'blur' }]">
<el-input size="small" placeholder="请输入链接" v-model="form.url"></el-input>
</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,
name: ''
},
form: {
type: '1',
pictureUrl: [],
url: '',
name: '',
appId: ''
},
typeDict: [{
dictName: '外链小程序',
dictValue: '0'
}, {
dictName: '外链h5',
dictValue: '1'
}],
isShowAdd: false,
colConfigs: [
{ prop: 'name', label: '专题名称' },
{ slot: 'img', align: 'center' },
{ prop: 'type', align: 'center', label: '类型', formart: v => v === '0' ? '小程序' : '链接' },
{ prop: 'url', align: 'center', label: '链接' },
{ prop: 'appId', align: 'center', label: '小程序appid' },
{ prop: 'createTime', align: 'center', label: '创建时间' }
],
id: '',
tableData: [],
total: 0,
loading: false
}
},
created () {
this.getList()
},
methods: {
getList () {
this.instance.post(`/app/appminitopicconfig/list`, null, {
params: {
...this.search
}
}).then(res => {
if (res.code == 0) {
this.tableData = res.data.records.map(v => {
return {
...v,
img: [{
url: v.pictureUrl
}]
}
})
this.total = res.data.total
this.loading = false
} else {
this.loading = false
}
}).catch(() => {
this.loading = false
})
},
onChange () {
this.form.url = ''
this.form.appId = ''
},
onConfirm () {
this.$refs.form.validate((valid) => {
if (valid) {
this.instance.post(`/app/appminitopicconfig/addOrUpdate`, {
...this.form,
id: this.id || '',
pictureUrl: this.form.pictureUrl[0].url
}).then(res => {
if (res.code === 0) {
this.$message.success('添加成功')
this.isShowAdd = false
this.getList()
}
})
}
})
},
edit (e) {
this.id = e.id
this.form = {
...e,
pictureUrl: [{
url: e.pictureUrl
}]
}
this.$nextTick(() => {
this.isShowAdd = true
})
},
onClose () {
this.id = ''
this.form.type = '1'
this.form.pictureUrl = []
this.form.url = ''
this.form.name = ''
this.form.appId = ''
},
remove (id) {
this.$confirm('确定删除该数据?').then(() => {
this.instance.post(`/app/appminitopicconfig/delete?ids=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('删除成功!')
this.getList()
}
})
})
}
}
}
</script>
<style scoped lang="scss">
</style>