Compare commits
57 Commits
feature/rs
...
0e35945d4a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e35945d4a | ||
|
|
0762067bec | ||
|
|
290ecb6823 | ||
|
|
ee15427e88 | ||
|
|
06fa7b636e | ||
|
|
225c0088e1 | ||
|
|
ae83152271 | ||
|
|
b1c0beb8f6 | ||
|
|
4d96417661 | ||
|
|
711db33df3 | ||
|
|
b7c0350134 | ||
|
|
9ff89c19f3 | ||
|
|
4684952973 | ||
|
|
4ead05b251 | ||
|
|
cf62d2f508 | ||
|
|
a894db0144 | ||
|
|
a748a8b337 | ||
|
|
37a6cb3457 | ||
|
|
045449331f | ||
|
|
4c72bd2ac9 | ||
|
|
b3942f5822 | ||
|
|
0b6bc910c4 | ||
|
|
a4233d5f2c | ||
|
|
31c874e1ba | ||
|
|
d40830188d | ||
|
|
e789570a1b | ||
|
|
0de94d76ee | ||
|
|
536f579523 | ||
|
|
c8d75ab72a | ||
|
|
ad9676c040 | ||
|
|
29bd119ff4 | ||
|
|
b07cca9bcf | ||
|
|
1c2364574d | ||
|
|
528082fc6b | ||
|
|
823c327894 | ||
|
|
39f6275e31 | ||
|
|
44d971998a | ||
|
|
6d7769e61a | ||
|
|
afe1df98f3 | ||
|
|
97bd799b6d | ||
|
|
3316b73450 | ||
|
|
eee06c837d | ||
|
|
c4ae782195 | ||
|
|
2b22db2bf3 | ||
|
|
30cf69df04 | ||
|
|
54352ece58 | ||
|
|
daffd10bce | ||
|
|
a22b574614 | ||
|
|
e3c79f232d | ||
|
|
1ae371cb30 | ||
|
|
96d24a8b22 | ||
|
|
34eb7320fa | ||
|
|
9fa56ad890 | ||
|
|
263dd9ea37 | ||
|
|
85de031db2 | ||
|
|
a6f11e56a7 | ||
|
|
15f6dc6499 |
5
.env.xumu
Normal file
5
.env.xumu
Normal file
@@ -0,0 +1,5 @@
|
||||
VUE_APP_SCOPE=xumu
|
||||
VUE_APP_API=http://192.168.1.87:12413
|
||||
VUE_APP_IS_SIMPLE_SERVER=1
|
||||
VUE_APP_PORT=12413
|
||||
VUE_APP_OMS_ID=2cd70a15-a3cf-4b4d-9a22-0f3b3a888b08 # oms定制方案的ID
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -30,3 +30,4 @@ yarn-error.log*
|
||||
/examples/router/apps.js
|
||||
/src/apps/
|
||||
/src/config.json
|
||||
/src/utils/apps.js
|
||||
|
||||
69
bin/build.js
69
bin/build.js
@@ -1,5 +1,6 @@
|
||||
const axios = require('axios')
|
||||
const {fsExtra, copyFiles} = require("./tools");
|
||||
const {fsExtra, copyFiles, findApp, chalkTag, fs} = require("./tools");
|
||||
const compiler = require('vue-template-compiler')
|
||||
const getBuildConfig = id => {
|
||||
axios.post('http://192.168.1.87:12525/node/custom/detail', null, {params: {id}}).then(res => {
|
||||
if (res?.data) {
|
||||
@@ -9,6 +10,68 @@ const getBuildConfig = id => {
|
||||
}
|
||||
})
|
||||
}
|
||||
const getAppInfo = (file, apps) => {
|
||||
if (/[\\\/](App[A-Z][^\\\/]+)\.vue$/g.test(file)) {
|
||||
const name = file.replace(/.+[\\\/](App[^\\\/]+)\.vue$/, '$1'),
|
||||
source = fs.readFileSync(file).toString(),
|
||||
parsed = compiler.parseComponent(source),
|
||||
script = parsed.script?.content || "",
|
||||
label = script.match(/label:[^,]+/)?.[0]?.replace(/.+["']([^"']+).+/, '$1')
|
||||
const paths = file.split(/[\\\/]/)
|
||||
apps.push({
|
||||
id: file.replace(/\.vue$/, '').replace(/[\\\/]/g, '_'),
|
||||
label: label || name,
|
||||
path: `/${file.replace(/\.vue$/, '').replace(/[\\\/]/g, '/')}`,
|
||||
workspace: paths.at(0),
|
||||
esm: file.replace(/[\\\/]/g, '/').substring(4),
|
||||
name
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置生成应用路由
|
||||
* @param {Object} config - 配置对象,用于定制化路由生成过程
|
||||
* @returns {Promise} - 返回一个Promise对象,表示路由生成完成
|
||||
*/
|
||||
const createRoutes = (config = {}) => {
|
||||
// 初始化路由数组
|
||||
const routes = []
|
||||
// 获取签到页面的路径,如果未指定,则使用默认路径
|
||||
let signPage = '../views/sign'
|
||||
let {signPage: sign, homePage: home = "console"} = config.extra || {}
|
||||
if (config.extra?.signPage) {
|
||||
signPage = `../apps/custom/${sign}/${sign}`
|
||||
}
|
||||
let homePage = `../views/console`
|
||||
if (config.extra?.homePage) {
|
||||
homePage = `../apps/custom/${home}/${home}`
|
||||
}
|
||||
// 查找并处理所有应用,将它们的信息添加到路由中
|
||||
return findApp("src/apps", app => getAppInfo(app, routes)).then(() => {
|
||||
// 生成并输出apps.js文件,定义所有应用的路由
|
||||
fsExtra.outputFile('src/utils/apps.js', `export default [
|
||||
{path: "/login", name: "登录", component: () => import('${signPage}')},
|
||||
{path: '/dv', name: '数据大屏入口', component: () => import('../views/dvIndex')},
|
||||
{path: '/v', name: 'Home', component: () => import('../views/home'), children: [
|
||||
{path:'/',name:'mainEntry', component:()=>import('../views/mainEntry'),children:[
|
||||
{name: "${home}", path: "${home}", component: () => import('${homePage}')},
|
||||
${routes.filter(e => ![sign, home].includes(e.name)).map(e => {
|
||||
// 解构每个路由的属性,用于生成路由配置
|
||||
const {name, label, esm} = e
|
||||
// 生成单个路由配置的字符串表示
|
||||
return `{name:"${name}",label:"${label}",path:"${name}",component:()=>import("../${esm}")}`
|
||||
}).join(',\n')},
|
||||
{path: '*',name: '404',component: ()=>import('../views/building')},
|
||||
]}
|
||||
]},
|
||||
{path: '/', name: "init"},
|
||||
|
||||
]`)
|
||||
// 扫描完毕,使用chalkTag标记任务完成
|
||||
chalkTag.done("扫描完毕")
|
||||
})
|
||||
}
|
||||
|
||||
const createPages = (config = {}) => {
|
||||
fsExtra.emptyDir("src/apps", err => {
|
||||
@@ -24,13 +87,13 @@ const createPages = (config = {}) => {
|
||||
copyFiles("src/apps/core", "packages/core"),
|
||||
copyFiles("src/apps/custom", `project/${customPath}`),
|
||||
...Object.keys(stdApps).map(e => copyFiles(`src/apps/${e.replace(/^packages[\\\/]/, '')}`, e)),
|
||||
]).then(() => fsExtra.ensureFile("src/apps/actions.js"))
|
||||
]).then(() => createRoutes(config)).then(() => fsExtra.ensureFile("src/apps/actions.js"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const start = () => {
|
||||
const buildId = process.argv[2] || 'f670cc46-7cf7-4a0f-86ee-3077044c0b17'
|
||||
const buildId = process.argv[2] || process.env.VUE_APP_OMS_ID || 'f670cc46-7cf7-4a0f-86ee-3077044c0b17'
|
||||
getBuildConfig(buildId)
|
||||
}
|
||||
start()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Message} from 'element-ui'
|
||||
import { Message } from 'element-ui'
|
||||
import instance from 'dui/lib/js/request'
|
||||
|
||||
let baseURLs = {
|
||||
@@ -11,7 +11,7 @@ instance.interceptors.request.use(config => {
|
||||
config.url = "/ns" + config.url
|
||||
}
|
||||
if (process.env.VUE_APP_IS_SIMPLE_SERVER == 1) {
|
||||
config.url = config.url.replace(/(app|auth|admin)\//, "api/")
|
||||
config.url = config.url.replace(/^\/(app|auth|admin)\//, "/api/")
|
||||
}
|
||||
return config
|
||||
}, error => Message.error(error))
|
||||
|
||||
@@ -7,10 +7,14 @@
|
||||
"dev": "vue-cli-service serve examples/main.js",
|
||||
"build": "vue-cli-service build",
|
||||
"oms": "vue-cli-service serve examples/main.js --mode oms",
|
||||
"xumu": "vue-cli-service serve examples/main.js --mode xumu",
|
||||
"sync": "node bin/appsSync.js",
|
||||
"preview": "node bin/build.js && vue-cli-service serve",
|
||||
"predev": "node bin/scanApps.js",
|
||||
"preoms": "dotenv -e .env.oms node bin/scanApps.js"
|
||||
"preoms": "dotenv -e .env.oms node bin/scanApps.js",
|
||||
"prexumu": "dotenv -e .env.xumu node bin/scanApps.js",
|
||||
"view:xumu": "vue-cli-service serve --mode xumu",
|
||||
"preview:xumu": "dotenv -e .env.xumu node bin/build.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
<el-button type="text" @click="toDetail(row.id)">详情</el-button>
|
||||
<el-button type="text" @click="remove(row.id)">删除</el-button>
|
||||
<el-button type="text" @click="gag(row.createUserId, row.blacklist)">{{ row.blacklist ? '解除禁言' : '禁言' }}</el-button>
|
||||
<el-button type="text" v-if="row.status<1" @click="admin(row)">审核</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -77,7 +78,7 @@
|
||||
},
|
||||
total: 0,
|
||||
colConfigs: [
|
||||
{ prop: 'content', label: '内容', align: 'left' },
|
||||
{ prop: 'content', label: '帖子内容', align: 'left' },
|
||||
{ prop: 'createUserName', label: '发帖人', align: 'center', width: '120' },
|
||||
{ prop: 'createUserAreaName', label: '所属地区', align: 'center' },
|
||||
{ prop: 'createTime', label: '创建时间', align: 'center' },
|
||||
@@ -85,6 +86,7 @@
|
||||
{ prop: 'appreciateCount', label: '点赞数', align: 'center', width: '120' },
|
||||
{ prop: 'sharedCount', label: '分享数', align: 'center', width: '120' },
|
||||
{ prop: 'blacklist', label: '状态', align: 'center', format: v => v ? '禁言' : '正常' },
|
||||
{ prop: 'status', label: '审核状态', align: 'center', width: '120', dict: 'auditStatus' },
|
||||
{ slot: 'options'},
|
||||
],
|
||||
tableData: [],
|
||||
@@ -100,7 +102,9 @@
|
||||
|
||||
created() {
|
||||
this.search.areaId = this.user.info.areaId
|
||||
this.getList()
|
||||
this.dict.load('auditStatus').then(() => {
|
||||
this.getList()
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
@@ -172,6 +176,34 @@
|
||||
id: id || ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
admin(row) {
|
||||
console.log(row)
|
||||
this.$confirm('是否审核通过该条帖子?', {
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: '通过',
|
||||
closeOnClickModal: true,
|
||||
cancelButtonText: '拒绝'
|
||||
}).then((e) => {
|
||||
this.instance.post(`/app/appneighborhoodassistance/examine?id=${row.id}&pass=1`).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('审核成功!')
|
||||
this.search.current = 1
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}).catch((e) => {
|
||||
if(e == 'cancel') {
|
||||
this.instance.post(`/app/appneighborhoodassistance/examine?id=${row.id}&pass=0`).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('审核成功!')
|
||||
this.search.current = 1
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,151 +1,27 @@
|
||||
<template>
|
||||
<section class="AppDictionary">
|
||||
<ai-list v-if="!showDetail">
|
||||
<ai-title slot="title" title="数据字典" isShowBottomBorder/>
|
||||
<template #content>
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
<el-button type="primary" size="small" icon="iconfont iconAdd" @click="addDict"
|
||||
v-if="$permissions('admin_sysdictionary_add')">添加
|
||||
</el-button>
|
||||
</template>
|
||||
<template #right>
|
||||
<el-input size="small" v-model="search.condition" placeholder="数据项" clearable
|
||||
@change="page.current=1,getDicts()" prefix-icon="iconfont iconSearch"/>
|
||||
<el-button type="primary" size="small" icon="iconfont iconSearch"
|
||||
@click="page.current=1,getDicts()">查询
|
||||
</el-button>
|
||||
<el-button size="small" icon="el-icon-refresh-right" @click="resetSearch">重置</el-button>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<el-table size="mini" :data="dictList" header-cell-class-name="table-header" tooltip-effect="light"
|
||||
row-class-name="table-row" cell-class-name="table-cell" @expand-change="getDictInfo">
|
||||
<el-table-column type="expand">
|
||||
<el-row slot-scope="{row}" type="flex" align="middle" style="flex-wrap: wrap">
|
||||
<el-tag v-for="(op,i) in row.detail||[]" :key="i" style="margin: 4px">{{ op.dictValue }}|{{ op.dictName }}
|
||||
{{ op.dictColor ? '| ' + op.dictColor : '' }}
|
||||
</el-tag>
|
||||
</el-row>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="数据项" prop="code"/>
|
||||
<el-table-column align="center" label="数据项名称" prop="name"/>
|
||||
<el-table-column align="center" label="操作">
|
||||
<div slot-scope="{row}">
|
||||
<el-button type="text" @click="openDetail(row.id)" v-text="'编辑'"
|
||||
v-if="$permissions('admin_sysdictionary_edit')"/>
|
||||
<el-button type="text" @click="handleDelete(row.id)" v-text="'删除'"
|
||||
v-if="$permissions('admin_sysdictionary_del')"/>
|
||||
</div>
|
||||
</el-table-column>
|
||||
<div slot="empty" class="no-data"></div>
|
||||
</el-table>
|
||||
<div class="pagination">
|
||||
<el-pagination background :current-page.sync="page.current" :total="page.total"
|
||||
layout="total,prev, pager, next,sizes, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
:page-size="page.size"
|
||||
:page-sizes="[10, 20, 50, 100,200]"
|
||||
@current-change="getDicts"/>
|
||||
</div>
|
||||
</template>
|
||||
</ai-list>
|
||||
<dict-detail v-else :instance="instance" :permissions="permissions"/>
|
||||
<component :is="currentPage" v-bind="$props"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DictDetail from "./dictDetail";
|
||||
import DictList from "@project/xumu/AppDictionary/dictList.vue";
|
||||
|
||||
export default {
|
||||
name: "AppDictionary",
|
||||
components: {DictDetail},
|
||||
label: "数据字典",
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function
|
||||
},
|
||||
computed: {
|
||||
showDetail() {
|
||||
return this.$route.hash == "#add"
|
||||
currentPage() {
|
||||
let {hash} = this.$route
|
||||
return hash == "#add" ? DictDetail : DictList
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
page: {
|
||||
current: 1,
|
||||
total: 0,
|
||||
size: 10
|
||||
},
|
||||
search: {
|
||||
condition: ""
|
||||
},
|
||||
dictList: [],
|
||||
id: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetSearch() {
|
||||
this.page.current = 1;
|
||||
this.search.condition = '';
|
||||
this.getDicts();
|
||||
},
|
||||
getDicts() {
|
||||
this.instance.post("/admin/dictionary/queryDictList", null, {
|
||||
params: {
|
||||
...this.page,
|
||||
name: this.search.condition
|
||||
}
|
||||
}).then(res => {
|
||||
this.dictList = res.data.records.map(e => {
|
||||
return {...e, detail: []}
|
||||
})
|
||||
this.page.total = res.data.total
|
||||
})
|
||||
},
|
||||
addDict() {
|
||||
this.$router.push({hash: "#add"})
|
||||
},
|
||||
handleDelete(id) {
|
||||
this.$confirm("确定要删除该数据项吗?", {
|
||||
type: "error"
|
||||
}).then(() => {
|
||||
this.instance.post("/admin/dictionary/deleteDict", null, {
|
||||
params: {id}
|
||||
}).then(res => {
|
||||
if (res?.code == 0) {
|
||||
this.getDicts();
|
||||
this.$message.success("删除成功!")
|
||||
}
|
||||
})
|
||||
}).catch(() => 0)
|
||||
},
|
||||
openDetail(id) {
|
||||
this.$router.push({query: {id}, hash: "#add"})
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.page.size = val;
|
||||
this.getDicts();
|
||||
},
|
||||
getDictInfo(row) {
|
||||
if (row.detail.length) {
|
||||
row.detail = []
|
||||
} else {
|
||||
this.getDict(row.id).then(res => {
|
||||
if (res && res.data) {
|
||||
row.detail = res.data.dictionaryDetails || []
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
getDict(dictionaryId) {
|
||||
return this.instance.post("/admin/dictionary/queryDictDetail", null, {
|
||||
params: {dictionaryId}
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getDicts()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
142
packages/core/AppDictionary/dictList.vue
Normal file
142
packages/core/AppDictionary/dictList.vue
Normal file
@@ -0,0 +1,142 @@
|
||||
<script>
|
||||
const columns = [
|
||||
{slot: "expand"},
|
||||
{label: "数据项", prop: "code"},
|
||||
{label: "数据项名称", prop: "name"},
|
||||
]
|
||||
export default {
|
||||
name: "dictList",
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
columns,
|
||||
page: {
|
||||
current: 1,
|
||||
total: 0,
|
||||
size: 10
|
||||
},
|
||||
search: {
|
||||
condition: ""
|
||||
},
|
||||
dictList: [],
|
||||
id: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetSearch() {
|
||||
this.page.current = 1;
|
||||
this.search.condition = '';
|
||||
this.getDicts();
|
||||
},
|
||||
getDicts() {
|
||||
this.instance.post("/admin/dictionary/queryDictList", null, {
|
||||
params: {
|
||||
...this.page,
|
||||
name: this.search.condition
|
||||
}
|
||||
}).then(res => {
|
||||
this.dictList = res.data.records.map(e => {
|
||||
return {...e, detail: []}
|
||||
})
|
||||
this.page.total = res.data.total
|
||||
})
|
||||
},
|
||||
addDict() {
|
||||
this.$router.push({hash: "#add"})
|
||||
},
|
||||
handleDelete(id) {
|
||||
this.$confirm("确定要删除该数据项吗?", {
|
||||
type: "error"
|
||||
}).then(() => {
|
||||
this.instance.post("/admin/dictionary/deleteDict", null, {
|
||||
params: {id}
|
||||
}).then(res => {
|
||||
if (res?.code == 0) {
|
||||
this.getDicts();
|
||||
this.$message.success("删除成功!")
|
||||
}
|
||||
})
|
||||
}).catch(() => 0)
|
||||
},
|
||||
openDetail(id) {
|
||||
this.$router.push({query: {id}, hash: "#add"})
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.page.size = val;
|
||||
this.getDicts();
|
||||
},
|
||||
getDictInfo(row) {
|
||||
if (row.detail.length) {
|
||||
row.detail = []
|
||||
} else {
|
||||
this.getDict(row.id).then(res => {
|
||||
if (res && res.data) {
|
||||
row.detail = res.data.dictionaryDetails || []
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
getDict(dictionaryId) {
|
||||
return this.instance.post("/admin/dictionary/queryDictDetail", null, {
|
||||
params: {dictionaryId}
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getDicts()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<section class="dictList">
|
||||
<ai-list>
|
||||
<ai-title slot="title" title="数据字典" isShowBottomBorder/>
|
||||
<template #content>
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
<el-button type="primary" size="small" icon="iconfont iconAdd" @click="addDict"
|
||||
v-if="$permissions('admin_sysdictionary_add')">添加
|
||||
</el-button>
|
||||
</template>
|
||||
<template #right>
|
||||
<el-input size="small" v-model="search.condition" placeholder="数据项" clearable
|
||||
@change="page.current=1,getDicts()" prefix-icon="iconfont iconSearch"/>
|
||||
<el-button type="primary" size="small" icon="iconfont iconSearch"
|
||||
@click="page.current=1,getDicts()">查询
|
||||
</el-button>
|
||||
<el-button size="small" icon="el-icon-refresh-right" @click="resetSearch">重置</el-button>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table :tableData="dictList" :colConfigs="columns" :dict="dict" @getList="getDicts"
|
||||
:total="page.total" :current.sync="page.current" :size.sync="page.size" :page-sizes="[10, 20, 50, 100,200]"
|
||||
@expand-change="getDictInfo">
|
||||
<el-table-column slot="expand" type="expand">
|
||||
<template slot-scope="{row}">
|
||||
<div class="flex" style="gap:4px">
|
||||
<el-tag v-for="(op,i) in row.detail||[]" :key="i">{{ [op.dictValue, op.dictName, op.dictColor].filter(Boolean).join("|") }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="options" label="操作" fixed="right" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<div class="table-options">
|
||||
<el-button type="text" @click="openDetail(row.id)" v-if="$permissions('admin_sysdictionary_edit')">编辑</el-button>
|
||||
<el-button type="text" @click="handleDelete(row.id)" v-if="$permissions('admin_sysdictionary_del')">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</template>
|
||||
</ai-list>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dictList {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="AppGridReview">
|
||||
<div class="AppFormReview">
|
||||
<keep-alive :include="['List']">
|
||||
<component ref="component" :is="component" :permissions="permissions " @change="onChange" :params="params" :instance="instance" :dict="dict"></component>
|
||||
</keep-alive>
|
||||
@@ -11,7 +11,7 @@
|
||||
import Detail from './components/Detail'
|
||||
|
||||
export default {
|
||||
name: 'AppGridReview',
|
||||
name: 'AppFormReview',
|
||||
label: '网格动态',
|
||||
|
||||
props: {
|
||||
@@ -56,7 +56,7 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.AppGridReview {
|
||||
.AppFormReview {
|
||||
height: 100%;
|
||||
background: #F3F6F9;
|
||||
overflow: auto;
|
||||
@@ -114,8 +114,8 @@
|
||||
this.instance.post(`/app/appgirdnews/examine?id=${this.params.id}&pass=${this.form.status}&opinion=${this.form.opinion}`).then(res => {
|
||||
if (res?.code == 0) {
|
||||
this.isShowExamine = false
|
||||
this.getDetail()
|
||||
this.$message.success('审核成功!')
|
||||
this.cancel(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
<el-table-column slot="scoringCycle" label="周期范围" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-if="row.parentRuleName == '工单处理'">-</span>
|
||||
<span v-else>{{row.numberLimit.length ? $dict.getLabel("integralRuleScoringCycle", row.scoringCycle)
|
||||
<span v-if="row.parentRuleName != '工单处理' && row.numberLimit">{{row.numberLimit.length ? $dict.getLabel("integralRuleScoringCycle", row.scoringCycle)
|
||||
: $dict.getLabel("integralRuleScoringCycle", row.scoringCycle) + row.numberLimit + "次"}}</span>
|
||||
<span v-if="row.parentRuleName != '工单处理' && !row.numberLimit">{{$dict.getLabel("integralRuleScoringCycle", row.scoringCycle) + "不限次"}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="integral" label="分值" align="center">
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
<el-input v-model="form.title" placeholder="请输入" show-word-limit maxlength="64"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="名额" prop="quota" style="width: 100%">
|
||||
<el-input-number v-model="form.quota" :min="1" :max="1000" label="请输入"></el-input-number>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="活动说明" style="width: 100%">
|
||||
<el-input type="textarea" :rows="5" v-model="form.detail" placeholder="请输入" show-word-limit maxlength="500"></el-input>
|
||||
</el-form-item>
|
||||
@@ -124,6 +128,7 @@ export default {
|
||||
return {
|
||||
form: {
|
||||
title: '',
|
||||
quota: 1,
|
||||
detail: '',
|
||||
lng: '',
|
||||
lat: '',
|
||||
@@ -144,6 +149,7 @@ export default {
|
||||
},
|
||||
formRules: {
|
||||
title: [{required: true, message: "请输入活动名称", trigger: "blur"}],
|
||||
quota: [{required: true, message: "请输入名额", trigger: "blur"}],
|
||||
location: [{required: true, validator: validLocation, trigger: "blur"}],
|
||||
clockRange: [{required: true, message: "请输入打卡范围", trigger: "blur"}],
|
||||
intoTime: [{required: true, message: "请选择进场打卡时间", trigger: "blur"}],
|
||||
@@ -185,17 +191,17 @@ export default {
|
||||
watch: {
|
||||
'form.intoTime': {
|
||||
handler(val) {
|
||||
if(val) {
|
||||
this.form.intoBegintime = val[0]
|
||||
this.form.intoEndtime = val[1]
|
||||
if (Array.isArray(val) && val.length >= 2) {
|
||||
this.form.intoBegintime = val[0];
|
||||
this.form.intoEndtime = val[1];
|
||||
}
|
||||
}
|
||||
},
|
||||
'form.exitTime': {
|
||||
handler(val) {
|
||||
if(val) {
|
||||
this.form.exitBegintime = val[0]
|
||||
this.form.exitEndtime = val[1]
|
||||
if (Array.isArray(val) && val.length >= 2) {
|
||||
this.form.exitBegintime = val[0];
|
||||
this.form.exitEndtime = val[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<template #content>
|
||||
<ai-wrapper>
|
||||
<ai-info-item label="活动名称" :value="info.title"></ai-info-item>
|
||||
<ai-info-item label="名额" :value="info.quota"></ai-info-item>
|
||||
<ai-info-item label="创建人" :value="info.createUserName"></ai-info-item>
|
||||
<ai-info-item label="活动说明" isLine :value="info.detail"></ai-info-item>
|
||||
<ai-info-item label="活动图片" isLine>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<section class="add">
|
||||
<ai-detail>
|
||||
<ai-title slot="title" :title="pageTitle" isShowBottomBorder/>
|
||||
<ai-title slot="title" :title="pageTitle" isShowBottomBorder />
|
||||
<template #content>
|
||||
<el-tabs tab-position="left">
|
||||
<el-tab-pane label="方案设置">
|
||||
@@ -9,23 +9,24 @@
|
||||
<ai-card title="基本信息">
|
||||
<template #content>
|
||||
<el-form-item label="项目/系统名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入" clearable/>
|
||||
<el-input v-model="form.name" placeholder="请输入" clearable />
|
||||
</el-form-item>
|
||||
<el-row type="flex">
|
||||
<div class="fill">
|
||||
<el-form-item label="系统类型" prop="type">
|
||||
<ai-select v-model="form.type" :selectList="dict.getDict('systemType')" @change="form.apps = []"/>
|
||||
<ai-select v-model="form.type" :selectList="dict.getDict('systemType')"
|
||||
@change="form.apps = []" />
|
||||
</el-form-item>
|
||||
<el-form-item label="更新项目路径" prop="dist">
|
||||
<el-input v-model="form.dist" placeholder="常填写nginx路径,下载包从这里取" clearable/>
|
||||
<el-input v-model="form.dist" placeholder="常填写nginx路径,下载包从这里取" clearable />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="fill mar-l16">
|
||||
<el-form-item label="库项目根路径" prop="customPath">
|
||||
<el-input v-model="form.customPath" placeholder="请输入" clearable/>
|
||||
<el-input v-model="form.customPath" placeholder="请输入" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="版本号" prop="version">
|
||||
<el-input v-model="form.version" placeholder="请输入" clearable/>
|
||||
<el-input v-model="form.version" placeholder="请输入" clearable />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-row>
|
||||
@@ -34,20 +35,22 @@
|
||||
<ai-card title="主库应用">
|
||||
<template #content>
|
||||
<ai-lib-table v-if="form.type" v-model="form.apps" v-bind="$props" multiple searchKey="name"
|
||||
:action="`/node/wechatapps/list?type=${form.type}&isMain=1`" border/>
|
||||
:action="`/node/wechatapps/list?type=${form.type}&isMain=1`" border />
|
||||
<ai-empty v-else>请先选择系统类型</ai-empty>
|
||||
</template>
|
||||
</ai-card>
|
||||
<component class="extraConfig" title="扩展设置" :is="extraConfig" v-model="form.extra"/>
|
||||
<component class="extraConfig" title="扩展设置" :is="extraConfig" v-model="form.extra" :appList="appList" />
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="方案应用" lazy>
|
||||
<ai-lib-table :meta="appList" customData :isShowPagination="false" v-bind="$props" disabled :colConfigs="appListConfigs">
|
||||
<ai-lib-table :meta="appList" customData :isShowPagination="false" v-bind="$props" disabled
|
||||
:colConfigs="appListConfigs">
|
||||
<template slot="options" slot-scope="{row}">
|
||||
<ai-dialog-btn text="编辑" :customFooter="false" dialogTitle="应用配置" width="500px" @onConfirm="handleAppEdit(row)">
|
||||
<ai-dialog-btn text="编辑" :customFooter="false" dialogTitle="应用配置" width="500px"
|
||||
@onConfirm="handleAppEdit(row)">
|
||||
<el-form size="small" label-width="80px">
|
||||
<el-form-item label="应用名称">
|
||||
<el-input v-model="row.label" clearable placeholder="请输入应用名称"/>
|
||||
<el-input v-model="row.label" clearable placeholder="请输入应用名称" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ai-dialog-btn>
|
||||
@@ -65,7 +68,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapActions} from "vuex"
|
||||
import { mapActions } from "vuex"
|
||||
import AiLibTable from "./AiLibTable";
|
||||
import webConfig from "./config/webConfig.vue";
|
||||
import wxcpConfig from "./config/wxcpConfig.vue";
|
||||
@@ -73,7 +76,7 @@ import wxmpConfig from "./config/wxmpConfig.vue";
|
||||
|
||||
export default {
|
||||
name: "add",
|
||||
components: {AiLibTable},
|
||||
components: { AiLibTable },
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
@@ -101,43 +104,29 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {apps: [], type: null, sysInfo: {}, customPath: ""},
|
||||
form: { apps: [], type: null, customPath: "", extra: { sysInfo: {} } },
|
||||
rules: {
|
||||
name: {required: true, message: "请输入"},
|
||||
type: {required: true, message: "请选择"},
|
||||
name: { required: true, message: "请输入" },
|
||||
type: { required: true, message: "请选择" },
|
||||
// customPath: {required: true, message: "请输入"},
|
||||
},
|
||||
appListConfigs: [
|
||||
{prop: 'label', label: "应用名称", render: (h, {row}) => h(row.tabbar ? 'b' : 'p', row.label + ` ${row.tabbar ? '(底部导航栏)' : ''}`)},
|
||||
{prop: 'project', label: "项目/框架"},
|
||||
{prop: 'category', label: "分类", dict: "appsCategory"},
|
||||
{prop: 'name', label: "模块名"}
|
||||
{ prop: 'label', label: "应用名称", render: (h, { row }) => h(row.tabbar ? 'b' : 'p', row.label + ` ${row.tabbar ? '(底部导航栏)' : ''}`) },
|
||||
{ prop: 'project', label: "项目/框架" },
|
||||
{ prop: 'category', label: "分类", dict: "appsCategory" },
|
||||
{ prop: 'name', label: "模块名" }
|
||||
],
|
||||
tabBar: {
|
||||
color: "#666666",
|
||||
selectedColor: "#197DF0",
|
||||
backgroundColor: "#ffffff",
|
||||
list: [
|
||||
{pagePath: "pages/AppHome/AppHome", text: "首页", iconPath: "static/TabBar/home.png", selectedIconPath: "static/TabBar/home_selected.png"},
|
||||
{pagePath: "pages/AppModules/AppModules", text: "应用", iconPath: "static/TabBar/service.png", selectedIconPath: "static/TabBar/service_selected.png"},
|
||||
{
|
||||
pagePath: "pages/AppEnteringVillage/AppEnteringVillage", text: "进村",
|
||||
iconPath: "static/TabBar/custom.png", selectedIconPath: "static/TabBar/custom_selected.png"
|
||||
},
|
||||
{pagePath: "pages/AppMine/AppMine", text: "我的", iconPath: "static/TabBar/me.png", selectedIconPath: "static/TabBar/me_selected.png"}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['closePage']),
|
||||
getDetail() {
|
||||
let {id} = this.$route.query
|
||||
let { id } = this.$route.query
|
||||
id && this.instance.post("/node/custom/detail", null, {
|
||||
params: {id}
|
||||
params: { id }
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.form = {...this.form, ...res.data}
|
||||
this.form = { ...this.form, ...res.data }
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -184,7 +173,7 @@ export default {
|
||||
:deep(.tabBarOptions) {
|
||||
flex-wrap: wrap;
|
||||
|
||||
.el-button--text + .el-button--text {
|
||||
.el-button--text+.el-button--text {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ export default {
|
||||
event: "input"
|
||||
},
|
||||
props: {
|
||||
form: Object,
|
||||
form: { default: () => ({ sysInfo: {} }) },
|
||||
title: String
|
||||
},
|
||||
watch: {
|
||||
form: {
|
||||
handler() {
|
||||
this.$emit("input", this.form)
|
||||
handler(v) {
|
||||
this.$emit("input", v)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
@@ -26,14 +26,14 @@ export default {
|
||||
<ai-dialog-btn text="设置系统信息" dialogTitle="系统信息">
|
||||
<el-form size="small" label-width="140px">
|
||||
<el-form-item label="系统标题">
|
||||
<el-input v-model="form.sysInfo.fullTitle" placeholder="请输入..." clearable/>
|
||||
<el-input v-model="form.sysInfo.fullTitle" placeholder="请输入..." clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="favicon">
|
||||
<el-input v-model="form.sysInfo.favicon" placeholder="请输入..." clearable/>
|
||||
<el-input v-model="form.sysInfo.favicon" placeholder="请输入..." clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="logo">
|
||||
<el-row type="flex">
|
||||
<el-input v-model="form.sysInfo.logo" placeholder="请输入..." clearable/>
|
||||
<el-input v-model="form.sysInfo.logo" placeholder="请输入..." clearable />
|
||||
<el-input class="mar-l10" v-model="form.sysInfo.logoText" placeholder="logo文字">
|
||||
<template #prepend>logo文字</template>
|
||||
</el-input>
|
||||
@@ -56,29 +56,30 @@ export default {
|
||||
<template #prepend>左上角副标题</template>
|
||||
</el-input>
|
||||
</el-row>
|
||||
<el-input class="mar-t10" type="textarea" rows="5" v-model="form.sysInfo.desc" placeholder="副标题" clearable/>
|
||||
<el-input class="mar-t10" type="textarea" rows="5" v-model="form.sysInfo.desc" placeholder="副标题"
|
||||
clearable />
|
||||
</el-form-item>
|
||||
<el-row type="flex">
|
||||
<div class="fill">
|
||||
<el-form-item label="版权所有">
|
||||
<el-input v-model="form.sysInfo.recordDesc" placeholder="请输入..." clearable/>
|
||||
<el-input v-model="form.sysInfo.recordDesc" placeholder="请输入..." clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="备案号">
|
||||
<el-input v-model="form.sysInfo.recordNo" placeholder="请输入..." clearable/>
|
||||
<el-input v-model="form.sysInfo.recordNo" placeholder="请输入..." clearable />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="fill">
|
||||
<el-form-item label="框架版本">
|
||||
<!--edition :版本,标准版:standard、上架版:saas 简易版(不带扫码):simple -->
|
||||
<el-input v-model="form.sysInfo.edition" placeholder="请输入..." clearable/>
|
||||
<el-input v-model="form.sysInfo.edition" placeholder="请输入..." clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="备案跳转链接">
|
||||
<el-input v-model="form.sysInfo.recordURL" placeholder="请输入..." clearable/>
|
||||
<el-input v-model="form.sysInfo.recordURL" placeholder="请输入..." clearable />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-form-item label="可信证书">
|
||||
<el-input type="textarea" v-model="form.sysInfo.ssl" placeholder="请输入可信证书的html代码" clearable rows="5"/>
|
||||
<el-input type="textarea" v-model="form.sysInfo.ssl" placeholder="请输入可信证书的html代码" clearable rows="5" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ai-dialog-btn>
|
||||
@@ -93,25 +94,23 @@ export default {
|
||||
<el-checkbox v-model="form.appQRCode">手机APP</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item label="接口是否单服务">
|
||||
<el-checkbox v-model="form.isSingleService"/>
|
||||
<el-checkbox v-model="form.isSingleService" />
|
||||
</el-form-item>
|
||||
<el-form-item label="域名根目录">
|
||||
<el-input v-model="form.base" clearable placeholder="填写域名根目录(baseURL)"/>
|
||||
<el-input v-model="form.base" clearable placeholder="填写域名根目录(baseURL)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="默认首页">
|
||||
<el-input v-model="form.homePage" clearable placeholder="填写应用的文件名"/>
|
||||
<el-input v-model="form.homePage" clearable placeholder="填写应用的文件名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="登录页">
|
||||
<el-input v-model="form.signPage" clearable placeholder="填写应用的文件名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开启百度流量">
|
||||
<el-checkbox v-model="form.hmt"/>
|
||||
<el-checkbox v-model="form.hmt" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否加载AI助手">
|
||||
<el-checkbox v-model="form.copilot"/>
|
||||
<el-checkbox v-model="form.copilot" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</ai-card>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.webConfig {
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,14 +10,20 @@ export default {
|
||||
},
|
||||
props: {
|
||||
form: Object,
|
||||
title: String
|
||||
title: String,
|
||||
appList: {default: () => []}
|
||||
},
|
||||
watch: {
|
||||
form: {
|
||||
handler() {
|
||||
this.$emit("input", this.form)
|
||||
handler(v) {
|
||||
this.$emit("input", v)
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
tabBar: {
|
||||
deep: true, handler(v) {
|
||||
this.$emit("input", {...this.form, tabBar: {...v, list: v.list.filter(e => !!e.pagePath) || []}})
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -57,6 +63,11 @@ export default {
|
||||
this.tabBar.list.splice(i, 1, this.tabBar.list[i + offset])
|
||||
this.tabBar.list.splice(i + offset, 1, row)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.form.tabBar?.list?.length > 0) {
|
||||
this.tabBar = this.form.tabBar
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
124
project/xumu/AppAccountConfigManage/AppAccountConfigManage.vue
Normal file
124
project/xumu/AppAccountConfigManage/AppAccountConfigManage.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<script>
|
||||
import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
name: "AppAccountConfigManage",
|
||||
label: "配置管理",
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
columns: [
|
||||
{label: "序号", type: "index"},
|
||||
{label: "账号", prop: "userName"},
|
||||
{label: "姓名", prop: "name"},
|
||||
{label: "角色", prop: "roleName"},
|
||||
{label: "所属端", prop: "type", dict: "roleType", width: 120, align: 'center'},
|
||||
{label: "状态", prop: "configStatus", dict: "configStatus", width: 120, align: 'center'},
|
||||
],
|
||||
tableData: [],
|
||||
page: {pageNum: 1, pageSize: 10, total: 0},
|
||||
search: {name: ""},
|
||||
dialog: false,
|
||||
userId: "",
|
||||
treeData: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['user'])
|
||||
},
|
||||
methods: {
|
||||
getTableData() {
|
||||
this.instance.post("/api/user/config/page", null, {
|
||||
params: {...this.page, ...this.search}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.tableData = res.data?.records
|
||||
this.page.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
getTreeData() {
|
||||
const {userId} = this
|
||||
this.instance.post("/api/siteUser/querySiteByUserId", null, {params: {userId}}).then(res => {
|
||||
if (res?.data) {
|
||||
this.treeData = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
handleDelete(node) {
|
||||
this.$confirm("是否要删除该节点?").then(() => {
|
||||
this.instance.post("/api/siteUser/del", null, {params: {ids: node.id}}).then(res => {
|
||||
if (res?.code == '0' && res?.data != 1) {
|
||||
this.$message.success("删除成功!")
|
||||
this.getTreeData()
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
createNode(data) {
|
||||
this.$prompt("请输入名称").then(({value}) => {
|
||||
const {userId} = this
|
||||
this.instance.post("/api/siteUser/add", null, {params: {name: value, parentId: data.id, userId}}).then(res => {
|
||||
if (res?.code == '0' && res?.data != 1) {
|
||||
this.$message.success("新增成功!")
|
||||
this.getTreeData()
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.dict.load("roleType", "configStatus")
|
||||
this.getTableData()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ai-page class="AppAccountConfigManage" :title="$options.label">
|
||||
<ai-search-bar>
|
||||
<template #right>
|
||||
<el-input size="small" placeholder="搜索账号" v-model="search.name" clearable
|
||||
@change="page.pageNum=1, getTableData()" @getList="getTableData"/>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
|
||||
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
|
||||
<el-table-column slot="options" label="操作" fixed="right" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<div class="table-options">
|
||||
<el-button type="text" @click="dialog=true,userId=row.id">配置</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
<ai-dialog v-model="dialog" title="认证材料" width="500px" @close="userId=''"
|
||||
@open="getTreeData" customFooter>
|
||||
<el-button class="mar-b8" type="primary" @click="createNode(treeData)">新增根节点</el-button>
|
||||
<el-tree :data="treeData" :props="{label:'name'}" default-expand-all>
|
||||
<template slot-scope="{node,data}">
|
||||
<div class="flex" style="width: 100%">
|
||||
<span class="fill" v-text="node.label"/>
|
||||
<el-button size="mini" type="text" @click="createNode(data)">增加子节点</el-button>
|
||||
<el-button size="mini" type="text" @click="handleDelete(data)">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-tree>
|
||||
<el-button slot="footer" @click="dialog=false,getTableData()">关闭</el-button>
|
||||
</ai-dialog>
|
||||
</ai-page>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.AppAccountConfigManage {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
245
project/xumu/AppAccountManage/AppAccountManage.vue
Normal file
245
project/xumu/AppAccountManage/AppAccountManage.vue
Normal file
@@ -0,0 +1,245 @@
|
||||
<template>
|
||||
<section class="AppAccountManage">
|
||||
<ai-page title="账号管理">
|
||||
<ai-area-tree :root-id="rootArea" slot="left" v-model="search.areaId" range="3" @input="page.pageNum=1,getTableData()"/>
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
<el-button type="primary" icon="iconfont iconAdd" @click="dialog = true">添加</el-button>
|
||||
<!-- <el-button type="primary" :disabled="!ids.toString()" @click="batchAllot">功能分配</el-button>-->
|
||||
</template>
|
||||
<template #right>
|
||||
<el-input size="small" placeholder="搜索姓名、手机号" v-model="search.condition" clearable
|
||||
@change="page.pageNum=1, getTableData()"/>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table :tableData="tableData" :total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize"
|
||||
@getList="getTableData" :col-configs="colConfigs" :dict="dict" @selection-change="v => ids = v.map(e => e.id)">
|
||||
<el-table-column slot="name" label="姓名" width="180px">
|
||||
<el-row type="flex" align="middle" slot-scope="{row}">
|
||||
<el-image class="avatar" :src="row.avatar" :preview-src-list="[row.avatar]">
|
||||
<el-image slot="error" src="https://cdn.cunwuyun.cn/dvcp/h5/defaultAvatar.png" alt=""/>
|
||||
</el-image>
|
||||
<div>{{ row.name }}</div>
|
||||
</el-row>
|
||||
</el-table-column>
|
||||
<el-table-column slot="options" align="center" label="操作" fixed="right" width="180px">
|
||||
<template slot-scope="{ row }">
|
||||
<div class="table-options">
|
||||
<el-button type="text" @click="changeEnable(row)">{{ row.status == 1 ? '禁用' : '启用' }}</el-button>
|
||||
<el-button type="text" @click="appAllot(row)">编辑</el-button>
|
||||
<el-button type="text" @click="handleDelete(row.id)">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</ai-page>
|
||||
<!--添加账号、功能分配-->
|
||||
<ai-dialog :title="dialogTitle" :visible.sync="dialog" width="60vw" @open="initDialogData"
|
||||
@onConfirm="updateAccount" @closed="dialogForm = {}">
|
||||
<el-form ref="updateAccountForm" :model="dialogForm" :rules="rules" size="small" label-width="120px" class="grid">
|
||||
<el-form-item required label="行政区划" prop="areaId">
|
||||
<ai-area-get v-model.trim="dialogForm.areaId" placeholder="请选择" :instance="instance"/>
|
||||
</el-form-item>
|
||||
<el-form-item required label="账户" prop="username">
|
||||
<el-input v-model.trim="dialogForm.username" placeholder="请输入..." clearable :maxLength="15"/>
|
||||
</el-form-item>
|
||||
<el-form-item required label="账号密码" prop="password" v-if="!isEdit" :rules="[{ required: true, message: '请输入密码' }]">
|
||||
<el-input v-model.trim="dialogForm.password" placeholder="请输入密码" clearable :minlength="6"/>
|
||||
</el-form-item>
|
||||
<el-form-item required label="角色" prop="roleId">
|
||||
<el-select placeholder="请选择角色" :value="dialogForm.roleId" filterable v-model="dialogForm.roleId" clearable>
|
||||
<el-option v-for="(op, i) in accountRoles" :key="i" :label="op.name" :value="op.id"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item required label="姓名" prop="name">
|
||||
<el-input v-model.trim="dialogForm.name" placeholder="请输入..." clearable :maxLength="15"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phone">
|
||||
<el-input v-model.trim="dialogForm.phone" placeholder="请输入..." clearable :maxLength="11"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ai-dialog>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
name: "AppAccountManage",
|
||||
label: "账号管理",
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function
|
||||
},
|
||||
computed: {
|
||||
...mapState(['user']),
|
||||
cascaderProps() {
|
||||
return {
|
||||
value: 'id',
|
||||
checkStrictly: true,
|
||||
emitPath: false
|
||||
}
|
||||
},
|
||||
isEdit() {
|
||||
return !!this.dialogForm.id
|
||||
},
|
||||
dialogTitle() {
|
||||
return this.isEdit ? '功能分配' : '添加账号'
|
||||
},
|
||||
colConfigs() {
|
||||
return [
|
||||
// {type: 'selection', align: 'center'},
|
||||
{label: "账号", slot: "username"},
|
||||
{label: "姓名", slot: "name"},
|
||||
{label: "联系方式", prop: "phone", align: 'center'},
|
||||
{label: "角色", prop: "roleName", align: 'center'},
|
||||
{label: "状态", prop: "status", align: 'center', dict: "enable"},
|
||||
{label: "认证状态", prop: "authStatus", align: 'center', dict: "authStatus"},
|
||||
{label: "配置状态", prop: "configStatus", align: 'center', dict: "configStatus"},
|
||||
{slot: "options"}
|
||||
]
|
||||
},
|
||||
rules() {
|
||||
return {
|
||||
username: [{required: true, message: "请输入账号"}],
|
||||
name: [{required: true, message: "请输入姓名"}],
|
||||
password: [{required: true, message: '请输入密码'}],
|
||||
areaId: [{required: true, message: "请选择行政区划"}],
|
||||
roleId: [{required: true, message: "请选择角色"}],
|
||||
// phone: [{required: true, message: "请输入手机号码"}]
|
||||
}
|
||||
},
|
||||
rootArea: v => v.user.info.areaId
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
accountRoles: [],
|
||||
page: {pageNum: 1, pageSize: 10, total: 0},
|
||||
dialog: false,
|
||||
dialogForm: {},
|
||||
tableData: [],
|
||||
search: {condition: ""},
|
||||
ids: [],
|
||||
form: {
|
||||
appids: [],
|
||||
userId: ''
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getTableData() {
|
||||
this.instance.post("/admin/user/page", null, {
|
||||
params: {...this.page, ...this.search}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.tableData = res.data?.records
|
||||
this.page.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
onConfirm() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.instance.post(`/api/sysuserwxmp/addOrUpdate`, {
|
||||
...this.form
|
||||
}).then(res => {
|
||||
if (res.code == 0) {
|
||||
this.getTableData()
|
||||
this.$message.success('提交成功!')
|
||||
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
initDialogData() {
|
||||
//用于优化初始化数据
|
||||
this.getAccountRoles()
|
||||
},
|
||||
getAccountRoles() {
|
||||
this.accountRoles.length == 0 && this.instance.post("/admin/role/list-all").then(res => {
|
||||
if (res?.data) {
|
||||
this.accountRoles = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
batchAllot() {
|
||||
this.dialog = true
|
||||
this.dialogForm = {areaId: this.user.info.areaId, ids: this.ids}
|
||||
},
|
||||
appAllot(row) {
|
||||
this.dialog = true
|
||||
this.dialogForm = JSON.parse(JSON.stringify({
|
||||
...row,
|
||||
areaId: row.areaId || this.user.info.areaId
|
||||
}));
|
||||
},
|
||||
// 修改
|
||||
updateAccount() {
|
||||
this.$refs.updateAccountForm.validate(v => {
|
||||
if (v) {
|
||||
this.instance.post("/admin/user/addOrEdit", this.dialogForm).then(res => {
|
||||
if (res?.code == 0) {
|
||||
this.dialog = false;
|
||||
this.$message.success("提交成功")
|
||||
this.getTableData();
|
||||
} else {
|
||||
this.$message.error(res?.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleDelete(ids) {
|
||||
this.$confirm("是否要删除该账号?").then(() => {
|
||||
this.instance.post("/admin/user/del", null, {
|
||||
params: {ids}
|
||||
}).then(res => {
|
||||
if (res?.code == 0) {
|
||||
this.getTableData();
|
||||
this.$message.success("删除成功!");
|
||||
}
|
||||
})
|
||||
}).catch(() => 0)
|
||||
},
|
||||
changeEnable(row) {
|
||||
const {status, id} = row
|
||||
this.$confirm(`是否要${status == 1 ? '禁用' : '启用'}该账号?`).then(() => {
|
||||
this.instance.post("/api/user/update-status", null, {params: {id}}).then(res => {
|
||||
if (res?.code == 0) {
|
||||
this.$message.success(`${status == 1 ? '禁用' : '启用'}成功!`)
|
||||
this.getTableData()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.dict.load("enable", "authStatus", "configStatus")
|
||||
this.getTableData()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AppAccountManage {
|
||||
height: 100%;
|
||||
|
||||
:deep(.avatar) {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
:deep(.el-form) {
|
||||
|
||||
.el-cascader,
|
||||
.el-select {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
43
project/xumu/AppAuthManage/AppAuthManage.vue
Normal file
43
project/xumu/AppAuthManage/AppAuthManage.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<script>
|
||||
import authAdd from "./authAdd.vue";
|
||||
import authList from "./authList.vue";
|
||||
export default {
|
||||
name: "AppAuthManage",
|
||||
label: "认证审核",
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function
|
||||
},
|
||||
computed: {
|
||||
currentPage() {
|
||||
let {hash} = this.$route
|
||||
return hash == "#add" ? authAdd : authList
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
certificates:[
|
||||
{label: "身份证(正面)", prop: "frontCard"},
|
||||
{label: "身份证(反面)", prop: "reverseCard"},
|
||||
{label: "营业执照", prop: "businessPic", permit: ["breed"]},
|
||||
{label: "畜禽经营许可证", prop: "breedPic", permit: ["breed"]},
|
||||
{label: "动物防疫条件许可证", prop: "prevention", permit: ["breed"]},
|
||||
{label: "组织机构证明", prop: "orgPic", permit: ["bank", "insurance"]},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="AppAuthManage">
|
||||
<component :is="currentPage" v-bind="$props"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.AppAuthManage {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
85
project/xumu/AppAuthManage/authAdd.vue
Normal file
85
project/xumu/AppAuthManage/authAdd.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "authAdd",
|
||||
props: {
|
||||
instance: Function,
|
||||
permissions: Function,
|
||||
dict: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
detail: {},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isAuditing: v => v.detail.auditStatus == 1
|
||||
},
|
||||
methods: {
|
||||
back(params = {}) {
|
||||
this.$router.push(params)
|
||||
},
|
||||
getDetail() {
|
||||
const {id} = this.$route.query
|
||||
this.instance.post("/api/user/auth/page", null, {params: {id}}).then(res => {
|
||||
if (res?.data?.records) {
|
||||
const detail = res.data.records[0] || {}
|
||||
let {picture = "{}"} = detail
|
||||
picture = JSON.parse(picture)
|
||||
this.detail = {...detail, ...picture}
|
||||
}
|
||||
})
|
||||
},
|
||||
getNeedCerts(type) {
|
||||
return this.$parent.certificates.filter(e => !e.permit || e.permit.includes(type))
|
||||
},
|
||||
handleAudit(auditStatus) {
|
||||
const auditLabels = {
|
||||
2: "同意通过", 3: "驳回"
|
||||
}
|
||||
this.$confirm(`是否要${auditLabels[auditStatus]}认证?`).then(() => {
|
||||
this.instance.post("/api/user/audit", null, {params:{
|
||||
id: this.detail.id, auditStatus
|
||||
}}).then(res => {
|
||||
if (res?.code == 0) {
|
||||
this.$confirm("是否要返回列表?","提交成功").then(() => this.back())
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.dict.load("auditStatus")
|
||||
this.getDetail()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ai-page title="认证材料" class="authAdd" showBack content-string="detail">
|
||||
<el-form size="small" label-position="top" :model="detail" ref="detail">
|
||||
<ai-card title="认证材料">
|
||||
<div class="grid">
|
||||
<el-form-item v-for="(op,i) in getNeedCerts(detail.type)" :key="i" v-bind="op" :rules="{required:true,message:`请上传${op.label}`,trigger:'change'}">
|
||||
<el-image :src="detail[op.prop]" :preview-src-list="[detail[op.prop]]"/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</ai-card>
|
||||
<ai-card title="备注说明">
|
||||
<div v-text="detail.remark"/>
|
||||
</ai-card>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<template v-if="isAuditing">
|
||||
<el-button type="primary" @click="handleAudit(2)">同意</el-button>
|
||||
<el-button type="danger" @click="handleAudit(3)">拒绝</el-button>
|
||||
</template>
|
||||
<el-button @click="back">关闭</el-button>
|
||||
</div>
|
||||
</ai-page>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.authAdd {
|
||||
}
|
||||
</style>
|
||||
130
project/xumu/AppAuthManage/authList.vue
Normal file
130
project/xumu/AppAuthManage/authList.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<script>
|
||||
|
||||
import AiSelect from "dui/packages/basic/AiSelect.vue";
|
||||
|
||||
const columns = [
|
||||
{label: "序号", type: "index"},
|
||||
{label: "账号", prop: "userName"},
|
||||
{label: "姓名", prop: "name"},
|
||||
{label: "角色", prop: "roleName"},
|
||||
{label: "所属端", prop: "type", dict: "roleType", width: 120, align: 'center'},
|
||||
{label: "状态", prop: "authStatus", dict: "authStatus", width: 120, align: 'center'},
|
||||
{label: "审核状态", prop: "auditStatus", dict: "auditStatus", width: 120, align: 'center'},
|
||||
]
|
||||
export default {
|
||||
name: "authList",
|
||||
components: {AiSelect},
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
columns,
|
||||
tableData: [],
|
||||
page: {pageNum: 1, pageSize: 10, total: 0},
|
||||
search: {name: ""},
|
||||
dialog: false,
|
||||
form: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getTableData() {
|
||||
this.instance.post("/api/user/auth/page", null, {
|
||||
params: {...this.page, ...this.search}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.tableData = res.data?.records.map(e => ({...e, permit: `${e.authStatus}` + e.auditStatus}))
|
||||
this.page.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
getNeedCerts(type) {
|
||||
return this.$parent.certificates.filter(e => !e.permit || e.permit.includes(type))
|
||||
},
|
||||
handleConfirm() {
|
||||
this.$refs.form.validate().then(() => {
|
||||
const {id, remark} = this.form, picture = {}
|
||||
this.$parent.certificates.forEach(e => {
|
||||
picture[e.prop] = this.form[e.prop]
|
||||
})
|
||||
this.instance.post("/api/user/savePicture", null, {
|
||||
params: {
|
||||
id, remark, picture: JSON.stringify(picture)
|
||||
}
|
||||
}).then(res => {
|
||||
if (res?.code == '0' && res?.data != 1) {
|
||||
this.dialog = false
|
||||
this.$message.success("提交成功!")
|
||||
this.getTableData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
handleUploadPics(row = {}) {
|
||||
let {id, type, remark, picture = "{}"} = row
|
||||
picture = JSON.parse(picture)
|
||||
this.form = {id, type, remark, ...picture}
|
||||
this.dialog = true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.dict.load("roleType", "authStatus", "auditStatus")
|
||||
this.getTableData()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ai-page class="authList" title="认证审核">
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
<ai-select v-model="search.authStatus" dict="authStatus" placeholder="状态"/>
|
||||
<ai-select v-model="search.auditStatus" dict="auditStatus" placeholder="审核状态"/>
|
||||
</template>
|
||||
<template #right>
|
||||
<el-input size="small" placeholder="搜索账号" v-model="search.name" clearable
|
||||
@change="page.pageNum=1, getTableData()" @getList="getTableData"/>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
|
||||
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
|
||||
<el-table-column slot="options" label="操作" fixed="right" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<div class="table-options">
|
||||
<el-button type="text" v-if="'12'.includes(row.permit)"
|
||||
@click="$router.push({query:{id:row.id},hash:'#add'})">查看
|
||||
</el-button>
|
||||
<el-button class="deleteBtn" type="text" v-if="'11'.includes(row.permit)"
|
||||
@click="$router.push({query:{id:row.id},hash:'#add'})">审核
|
||||
</el-button>
|
||||
<el-button type="text" v-if="'00|13'.includes(row.permit)"
|
||||
@click="handleUploadPics(row)">认证材料
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
<ai-dialog v-model="dialog" title="认证材料" width="60%" @close="form={}" @confirm="handleConfirm">
|
||||
<el-form class="grid c-3" :model="form" ref="form" label-width="160px">
|
||||
<el-form-item v-for="(op,i) in getNeedCerts(form.type)" :key="i" v-bind="op" :rules="{required:true,message:`请上传${op.label}`,trigger:'change'}">
|
||||
<ai-uploader v-model="form[op.prop]" valueIsUrl :limit="1" :instance="instance"/>
|
||||
</el-form-item>
|
||||
<el-form-item class="row" label="备注说明" prop="remark">
|
||||
<el-input type="textarea" :rows="2" v-model="form.remark" placeholder="备注说明具体情况"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ai-dialog>
|
||||
</ai-page>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.authList {
|
||||
height: 100%;
|
||||
|
||||
.deleteBtn {
|
||||
color: $errorColor;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
32
project/xumu/AppDictionary/AppDictionary.vue
Normal file
32
project/xumu/AppDictionary/AppDictionary.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<section class="AppDictionary">
|
||||
<component :is="currentPage" v-bind="$props"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DictDetail from "./dictDetail";
|
||||
import DictList from "@project/xumu/AppDictionary/dictList.vue";
|
||||
|
||||
export default {
|
||||
name: "AppDictionary",
|
||||
label: "数据字典",
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function
|
||||
},
|
||||
computed: {
|
||||
currentPage() {
|
||||
let {hash} = this.$route
|
||||
return hash == "#add" ? DictDetail : DictList
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AppDictionary {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
202
project/xumu/AppDictionary/dictDetail.vue
Normal file
202
project/xumu/AppDictionary/dictDetail.vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<section class="dictDetail">
|
||||
<ai-detail>
|
||||
<ai-title slot="title" title="字典信息" isShowBottomBorder isShowBack @onBackClick="$router.push({})"/>
|
||||
<template #content>
|
||||
<ai-card title="基本信息">
|
||||
<template #content>
|
||||
<el-form ref="dictDetailForm" :model="form" :rules="rules" size="small" label-width="110px">
|
||||
<el-form-item required label="数据项:" prop="code">
|
||||
<el-input v-model="form.code" style="width: 259px;" clearable
|
||||
placeholder="请输入..."/>
|
||||
</el-form-item>
|
||||
<el-form-item required label="数据项名称:" prop="name">
|
||||
<el-input v-model="form.name" style="width: 259px;" clearable
|
||||
placeholder="请输入..."/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
</ai-card>
|
||||
<ai-card title="数据值" v-if="$route.query.id">
|
||||
<template #right>
|
||||
<el-button type="text" icon="iconfont iconAdd"
|
||||
@click="form.dictionaryDetails.push({name:'',value:'',editable:true})"> 添加
|
||||
</el-button>
|
||||
</template>
|
||||
<template #content>
|
||||
<el-table border :data="form.dictionaryDetails" header-cell-class-name="table-header"
|
||||
cell-class-name="table-cell">
|
||||
<el-table-column align="center" label="值">
|
||||
<div slot-scope="{row}">
|
||||
<el-input size="small" v-if="row.editable" v-model="row.value" clearable/>
|
||||
<span v-else>{{ row.dictValue }}</span>
|
||||
</div>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="描述">
|
||||
<div slot-scope="{row}">
|
||||
<el-input size="small" v-if="row.editable" v-model="row.name" clearable/>
|
||||
<span v-else>{{ row.dictName }}</span>
|
||||
</div>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="颜色">
|
||||
<div slot-scope="{row}">
|
||||
<el-color-picker v-if="row.editable" v-model="row.dictColor" size="medium"></el-color-picker>
|
||||
<span v-else>{{ row.dictColor || '未设置' }}</span>
|
||||
</div>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="操作" width="109px">
|
||||
<div slot-scope="{row,$index}">
|
||||
<section v-if="row.editable">
|
||||
<el-button style="color: #2EA222" type="text" icon="iconfont iconCorrect"
|
||||
@click="addDict(row)"/>
|
||||
<el-button style="color: #f46" type="text" icon="iconfont iconClean"
|
||||
@click="cancelEdit(row,$index)"/>
|
||||
</section>
|
||||
<section v-else>
|
||||
<el-button class="dict-detail-operation" type="text" icon="iconfont iconEdit"
|
||||
@click="editDetail(row)"/>
|
||||
<el-button class="dict-detail-operation" type="text" icon="iconfont iconDelete"
|
||||
@click="delDictValue(row.id)"/>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
</ai-card>
|
||||
</template>
|
||||
<template #footer>
|
||||
<el-button @click="$router.push({})">返回</el-button>
|
||||
<el-button type="primary" @click="modifyDict">保存</el-button>
|
||||
</template>
|
||||
</ai-detail>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "dictDetail",
|
||||
props: {
|
||||
instance: Function,
|
||||
permissions: Function
|
||||
},
|
||||
computed: {
|
||||
rules() {
|
||||
return {
|
||||
code: [
|
||||
{required: true, message: "请填写数据项"}
|
||||
],
|
||||
name: [
|
||||
{required: true, message: "请填写数据项名称"}
|
||||
],
|
||||
// dictionaryDetails: [
|
||||
// {
|
||||
// validator: (r, v, cb) => {
|
||||
// if (v.every(item => item.dictName && item.dictValue)) {
|
||||
// cb()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
code: "",
|
||||
name: "",
|
||||
dictionaryDetails: []
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.$route.query.id) this.getDict()
|
||||
},
|
||||
methods: {
|
||||
getDict() {
|
||||
this.instance.post("/admin/dictionary/queryDictDetail", null, {
|
||||
params: {dictionaryId: this.$route.query.id}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
res.data.dictionaryDetails = res.data.dictionaryDetails.map(d => {
|
||||
return {
|
||||
...d,
|
||||
editable: false,
|
||||
name: "",
|
||||
value: ""
|
||||
}
|
||||
})
|
||||
this.form = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
delDictValue(id) {
|
||||
this.$confirm("是否要删除该字典值", {
|
||||
type: 'error'
|
||||
}).then(() => {
|
||||
this.instance.post("/admin/dictionary/deletevalue", null, {
|
||||
params: {id}
|
||||
}).then(res => {
|
||||
if (res?.code == 0) {
|
||||
this.$message.success("删除成功!")
|
||||
this.getDict()
|
||||
}
|
||||
})
|
||||
}).catch(() => 0)
|
||||
},
|
||||
editDetail(row) {
|
||||
row.editable = true
|
||||
row.name = row.dictName
|
||||
row.value = row.dictValue
|
||||
},
|
||||
addDict(row) {
|
||||
row.dictValue = row.value
|
||||
row.dictName = row.name
|
||||
row.dictionaryId = this.form.id
|
||||
this.instance.post("/admin/dictionary/updateDetail", row).then(res => {
|
||||
row.editable = false
|
||||
row = res.data.data
|
||||
this.$message.success("提交成功!")
|
||||
})
|
||||
},
|
||||
cancelEdit(row, index) {
|
||||
if (row.id) {
|
||||
row.editable = false
|
||||
} else {
|
||||
this.form.dictionaryDetails.splice(index, 1)
|
||||
}
|
||||
},
|
||||
modifyDict() {
|
||||
this.$refs.dictDetailForm.validate(v => {
|
||||
if (v) {
|
||||
this.instance.post("/admin/dictionary/updateDict", this.form).then(res => {
|
||||
if (res?.code == 0) {
|
||||
this.$message.success("提交成功!")
|
||||
this.$router.push({})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dictDetail {
|
||||
height: 100%;
|
||||
|
||||
:deep( .el-table__row ){
|
||||
|
||||
.el-input__inner {
|
||||
padding: 0 30px;
|
||||
border: none;
|
||||
text-align: center;
|
||||
background: #ddd;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
142
project/xumu/AppDictionary/dictList.vue
Normal file
142
project/xumu/AppDictionary/dictList.vue
Normal file
@@ -0,0 +1,142 @@
|
||||
<script>
|
||||
const columns = [
|
||||
{slot: "expand"},
|
||||
{label: "数据项", prop: "code"},
|
||||
{label: "数据项名称", prop: "name"},
|
||||
]
|
||||
export default {
|
||||
name: "dictList",
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
columns,
|
||||
page: {
|
||||
current: 1,
|
||||
total: 0,
|
||||
size: 10
|
||||
},
|
||||
search: {
|
||||
condition: ""
|
||||
},
|
||||
dictList: [],
|
||||
id: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetSearch() {
|
||||
this.page.current = 1;
|
||||
this.search.condition = '';
|
||||
this.getDicts();
|
||||
},
|
||||
getDicts() {
|
||||
this.instance.post("/admin/dictionary/queryDictList", null, {
|
||||
params: {
|
||||
...this.page,
|
||||
name: this.search.condition
|
||||
}
|
||||
}).then(res => {
|
||||
this.dictList = res.data.records.map(e => {
|
||||
return {...e, detail: []}
|
||||
})
|
||||
this.page.total = res.data.total
|
||||
})
|
||||
},
|
||||
addDict() {
|
||||
this.$router.push({hash: "#add"})
|
||||
},
|
||||
handleDelete(id) {
|
||||
this.$confirm("确定要删除该数据项吗?", {
|
||||
type: "error"
|
||||
}).then(() => {
|
||||
this.instance.post("/admin/dictionary/deleteDict", null, {
|
||||
params: {id}
|
||||
}).then(res => {
|
||||
if (res?.code == 0) {
|
||||
this.getDicts();
|
||||
this.$message.success("删除成功!")
|
||||
}
|
||||
})
|
||||
}).catch(() => 0)
|
||||
},
|
||||
openDetail(id) {
|
||||
this.$router.push({query: {id}, hash: "#add"})
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.page.size = val;
|
||||
this.getDicts();
|
||||
},
|
||||
getDictInfo(row) {
|
||||
if (row.detail.length) {
|
||||
row.detail = []
|
||||
} else {
|
||||
this.getDict(row.id).then(res => {
|
||||
if (res && res.data) {
|
||||
row.detail = res.data.dictionaryDetails || []
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
getDict(dictionaryId) {
|
||||
return this.instance.post("/admin/dictionary/queryDictDetail", null, {
|
||||
params: {dictionaryId}
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getDicts()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<section class="dictList">
|
||||
<ai-list>
|
||||
<ai-title slot="title" title="数据字典" isShowBottomBorder/>
|
||||
<template #content>
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
<el-button type="primary" size="small" icon="iconfont iconAdd" @click="addDict"
|
||||
v-if="$permissions('admin_sysdictionary_add')">添加
|
||||
</el-button>
|
||||
</template>
|
||||
<template #right>
|
||||
<el-input size="small" v-model="search.condition" placeholder="数据项" clearable
|
||||
@change="page.current=1,getDicts()" prefix-icon="iconfont iconSearch"/>
|
||||
<el-button type="primary" size="small" icon="iconfont iconSearch"
|
||||
@click="page.current=1,getDicts()">查询
|
||||
</el-button>
|
||||
<el-button size="small" icon="el-icon-refresh-right" @click="resetSearch">重置</el-button>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table :tableData="dictList" :colConfigs="columns" :dict="dict" @getList="getDicts"
|
||||
:total="page.total" :current.sync="page.current" :size.sync="page.size" :page-sizes="[10, 20, 50, 100,200]"
|
||||
@expand-change="getDictInfo">
|
||||
<el-table-column slot="expand" type="expand">
|
||||
<template slot-scope="{row}">
|
||||
<div class="flex" style="gap:4px">
|
||||
<el-tag v-for="(op,i) in row.detail||[]" :key="i">{{ [op.dictValue, op.dictName, op.dictColor].filter(Boolean).join("|") }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="options" label="操作" fixed="right" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<div class="table-options">
|
||||
<el-button type="text" @click="openDetail(row.id)" v-if="$permissions('admin_sysdictionary_edit')">编辑</el-button>
|
||||
<el-button type="text" @click="handleDelete(row.id)" v-if="$permissions('admin_sysdictionary_del')">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</template>
|
||||
</ai-list>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dictList {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
36
project/xumu/AppEarTag/AppEarTag.vue
Normal file
36
project/xumu/AppEarTag/AppEarTag.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<script>
|
||||
import add from "./etAdd.vue";
|
||||
import list from "./etList.vue";
|
||||
|
||||
export default {
|
||||
name: "AppEarTag",
|
||||
label: "耳标登记",
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function
|
||||
},
|
||||
computed: {
|
||||
currentPage() {
|
||||
let {hash} = this.$route
|
||||
return hash == "#add" ? add : list
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="AppEarTag">
|
||||
<component :is="currentPage" v-bind="$props"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.AppEarTag {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
164
project/xumu/AppEarTag/etAdd.vue
Normal file
164
project/xumu/AppEarTag/etAdd.vue
Normal file
@@ -0,0 +1,164 @@
|
||||
<script>
|
||||
import {mapState} from "vuex"
|
||||
import AiSelect from "dui/packages/basic/AiSelect.vue";
|
||||
|
||||
const columns = [
|
||||
{label: "序号", type: "index"},
|
||||
{label: "生物芯片耳标号", prop: "biochipEarNumber", edit: 1},
|
||||
{label: "电子耳标号", prop: "electronicEarNumber", edit: 1},
|
||||
{label: "原厂耳标号", prop: "originalEarNumber", edit: 1},
|
||||
{label: "戴耳标照片", prop: "picture", upload: {valueIsUrl: !0}},
|
||||
{label: "品种", prop: "variety", select: {dict: "variety"}},
|
||||
{label: "类别", prop: "category", select: {dict: "category"}},
|
||||
{label: "日龄/天", prop: "age", num: 1},
|
||||
{label: "体重/公斤", prop: "weight", num: 1},
|
||||
]
|
||||
export default {
|
||||
name: "etAdd",
|
||||
components: {AiSelect},
|
||||
props: {
|
||||
instance: Function,
|
||||
permissions: Function,
|
||||
dict: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
detail: {detailList: []},
|
||||
columns,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(["user"]),
|
||||
userinfo: v => v.user.info || {},
|
||||
isAdd: v => !v.$route.query.id,
|
||||
isEdit: v => v.$route.query.edit == 1,
|
||||
pageTitle: v => {
|
||||
const appName = v.$parent.menuName || v.$parent.$options.label
|
||||
return v.$route.query.id ? v.isEdit ? `编辑${appName}` : `${appName}详情` : `新增${appName}`
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
back(params = {}) {
|
||||
this.$router.push(params)
|
||||
},
|
||||
getDetail() {
|
||||
const {id} = this.$route.query
|
||||
return id && this.instance.post("/api/breed/earTag/page", {id}).then(res => {
|
||||
if (res?.data?.records) {
|
||||
const detail = res.data.records[0] || {}
|
||||
return this.detail = {detailList: [], ...detail}
|
||||
}
|
||||
})
|
||||
},
|
||||
handleDelete(index) {
|
||||
this.$confirm("确定删除该条数据?").then(() => {
|
||||
this.detail.detailList.splice(index, 1)
|
||||
})
|
||||
},
|
||||
submit() {
|
||||
this.$refs.detail.validate().then(() => {
|
||||
this.instance.post("/api/breed/earTag/addOrEdit", this.detail).then(res => {
|
||||
if (res?.code == 0) {
|
||||
this.$confirm("是否要返回列表?", "提交成功").then(() => this.back())
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.dict.load("auditStatus", "category", "variety")
|
||||
this.getDetail()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ai-page :title="pageTitle" class="etAdd" showBack content-string="blank">
|
||||
<el-form size="small" label-width="120px" :model="detail" ref="detail">
|
||||
<ai-card title="基础信息">
|
||||
<div class="grid c-3">
|
||||
<template v-if="isAdd">
|
||||
<el-form-item label="养殖场" prop="farmId">
|
||||
<ai-select v-model="detail.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${userinfo.id}`" :prop="{label:'name'}"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="养殖舍" prop="houseId">
|
||||
<ai-select v-model="detail.houseId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${detail.farmId||-1}`" :prop="{label:'name'}"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="养殖栏" prop="penId">
|
||||
<ai-select v-model="detail.penId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${detail.houseId||-1}`" :prop="{label:'name'}"/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-form-item label="养殖场" prop="farmId">
|
||||
<b v-text="detail.farmName"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="养殖舍" prop="houseId">
|
||||
<b v-text="detail.houseName"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="养殖栏" prop="penId">
|
||||
<b v-text="detail.penName"/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</div>
|
||||
</ai-card>
|
||||
<ai-card title="耳标录入">
|
||||
<template v-if="isAdd">
|
||||
<el-button type="text" slot="right" @click="detail.detailList.push({})">新增</el-button>
|
||||
<ai-table :tableData="detail.detailList" :colConfigs="columns" :isShowPagination="!1">
|
||||
<el-table-column slot="options" label="操作" fixed="right" align="center">
|
||||
<template slot-scope="{row,$index}">
|
||||
<div class="table-options">
|
||||
<el-button type="text" class="deleteBtn" @click="handleDelete($index)">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</template>
|
||||
<div class="grid" v-else>
|
||||
<el-form-item label="生物芯片耳标号">
|
||||
<el-input v-if="isEdit" v-model="detail.biochipEarNumber" placeholder="请输入" clearable/>
|
||||
<b v-else v-text="detail.biochipEarNumber"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="电子耳标号" prop="electronicEarNumber">
|
||||
<el-input v-if="isEdit" v-model="detail.electronicEarNumber" placeholder="请输入" clearable/>
|
||||
<b v-else v-text="detail.electronicEarNumber"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="原厂耳标号" prop="originalEarNumber">
|
||||
<el-input v-if="isEdit" v-model="detail.originalEarNumber" placeholder="请输入" clearable/>
|
||||
<b v-else v-text="detail.originalEarNumber"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="戴耳标照片" class="row">
|
||||
<el-image :src="detail.picture" :preview-src-list="[detail.picture]"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="品种" prop="variety">
|
||||
<ai-select v-if="isEdit" v-model="detail.variety" dict="variety"/>
|
||||
<b v-else v-text="detail.variety"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="类别" prop="category">
|
||||
<ai-select v-if="isEdit" v-model="detail.category" dict="category"/>
|
||||
<b v-else v-text="detail.category"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="日龄/天">
|
||||
<el-input v-if="isEdit" v-model.number="detail.age" placeholder="请输入" clearable/>
|
||||
<b v-else v-text="detail.age"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="体重/公斤">
|
||||
<el-input v-if="isEdit" v-model.number="detail.weight" placeholder="请输入" clearable/>
|
||||
<b v-else v-text="detail.weight"/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</ai-card>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<template v-if="isEdit||isAdd">
|
||||
<el-button type="primary" @click="submit">提交</el-button>
|
||||
</template>
|
||||
<el-button @click="back">返回</el-button>
|
||||
</div>
|
||||
</ai-page>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.etAdd {
|
||||
}
|
||||
</style>
|
||||
121
project/xumu/AppEarTag/etList.vue
Normal file
121
project/xumu/AppEarTag/etList.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<script>
|
||||
import {mapState} from "vuex"
|
||||
|
||||
const columns = [
|
||||
{label: "序号", type: "index"},
|
||||
{label: "养殖场", prop: "userName", format: (v, row) => `${[row.farmName, row.houseName, row.penName].join("-")}`},
|
||||
{label: "生物芯片耳标号", prop: "biochipEarNumber"},
|
||||
{label: "登记时间", prop: "createTime"},
|
||||
{label: "日龄/天", prop: "age", width: 120, align: 'right'},
|
||||
{label: "登记体重", prop: "weight", width: 120, align: 'right'},
|
||||
{label: "戴标员", prop: "userName", width: 120, align: 'center'},
|
||||
]
|
||||
export default {
|
||||
name: "etList",
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
columns,
|
||||
tableData: [],
|
||||
page: {pageNum: 1, pageSize: 10, total: 0},
|
||||
search: {},
|
||||
dialog: false,
|
||||
form: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['user']),
|
||||
userinfo: v => v.user.info || {}
|
||||
},
|
||||
watch: {
|
||||
search: {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.page.pageNum = 1
|
||||
this.getTableData()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getTableData() {
|
||||
this.instance.post("/api/breed/earTag/page", {...this.page, ...this.search}).then(res => {
|
||||
if (res?.data) {
|
||||
this.tableData = res.data?.records
|
||||
this.page.total = res.data.total
|
||||
}
|
||||
})
|
||||
},
|
||||
handleDelete(id) {
|
||||
this.$confirm("确定删除该条数据?").then(() => {
|
||||
this.instance.delete("/api/breed/earTag/del", null, {params: {id}}).then(res => {
|
||||
if (res?.code == '0' && res?.data != 1) {
|
||||
this.$message.success("删除成功")
|
||||
this.getTableData()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.dict.load("auditStatus", "category", "variety")
|
||||
this.getTableData()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ai-page class="etList" title="耳标登记">
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
<ai-select placeholder="全部机构" v-model="search.userId" :instance="instance" :action="`/api/breed/earTag/getOrgList`" :prop="{label:'name'}" readonly/>
|
||||
<ai-select placeholder="全部养殖场" v-model="search.farmId" :instance="instance" :action="`/api/siteUser/querySiteByUserId?userId=${search.userId||''}`" :prop="{label:'name'}"/>
|
||||
<ai-select placeholder="全部养殖舍" v-model="search.houseId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.farmId||-1}`" :prop="{label:'name'}"/>
|
||||
<ai-select placeholder="全部养殖栏" v-model="search.penId" :instance="instance" :action="`/api/siteUser/querySiteById?id=${search.houseId||-1}`" :prop="{label:'name'}"/>
|
||||
<el-input placeholder="戴标员" v-model="search.userName" dict="authStatus" size="small" clearable/>
|
||||
<el-input placeholder="生物芯片耳标号" v-model="search.biochipEarNumber" dict="authStatus" size="small" clearable/>
|
||||
<ai-select placeholder="全部类别" v-model="search.category" dict="category"/>
|
||||
<ai-select placeholder="全部品种" v-model="search.variety" dict="variety"/>
|
||||
<ai-search label="登记日期">
|
||||
<el-date-picker v-model="search.beginDate" type="datetime" placeholder="开始日期" size="small"/>
|
||||
<el-date-picker v-model="search.endDate" type="datetime" placeholder="结束日期" size="small"/>
|
||||
</ai-search>
|
||||
<ai-search label="日龄">
|
||||
<el-input placeholder="最小日龄" v-model="search.beginAge" size="small" clearable/>
|
||||
<el-input placeholder="最大日龄" v-model="search.endAge" size="small" clearable/>
|
||||
</ai-search>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
<el-button type="primary" icon="iconfont iconAdd" @click="$router.push({hash:'#add'})">新增</el-button>
|
||||
<ai-download :instance="instance" url="/api/breed/earTag/export" :params="{...search,...page}" :fileName="`耳标登记导出表-${Date.now()}`"/>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table :tableData="tableData" :colConfigs="columns" :dict="dict" @getList="getTableData"
|
||||
:total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize">
|
||||
<el-table-column slot="options" label="操作" fixed="right" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<div class="table-options">
|
||||
<el-button type="text" @click="$router.push({hash:'#add',query:{id:row.id}})">查看</el-button>
|
||||
<el-button type="text" @click="$router.push({hash:'#add',query:{id:row.id,edit:1}})">编辑</el-button>
|
||||
<el-button type="text" class="deleteBtn" @click="handleDelete(row.id)">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
</ai-page>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.etList {
|
||||
height: 100%;
|
||||
|
||||
.deleteBtn {
|
||||
color: $errorColor;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
416
project/xumu/AppRoleRightsManager/AppRoleRightsManager.vue
Normal file
416
project/xumu/AppRoleRightsManager/AppRoleRightsManager.vue
Normal file
@@ -0,0 +1,416 @@
|
||||
<template>
|
||||
<section class="AppRoleRightsManager">
|
||||
<ai-list v-if="!showDetail">
|
||||
<ai-title slot="title" title="角色管理" isShowBottomBorder/>
|
||||
<template #content>
|
||||
<ai-search-bar>
|
||||
<template #left>
|
||||
<el-button size="small" type="primary" icon="iconfont iconAdd"
|
||||
@click="$router.push({hash:'#add'})"
|
||||
v-if="$permissions('admin_sysapprole_add')">
|
||||
添加
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
icon="iconfont iconDelete"
|
||||
:disabled="!multipleSelection.length"
|
||||
class="del-btn-list"
|
||||
@click="deleteApp('all')"
|
||||
v-if="$permissions('admin_sysapprole_del')"
|
||||
>删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #right>
|
||||
<el-input
|
||||
size="small"
|
||||
v-model="search.roleName"
|
||||
placeholder="角色名称"
|
||||
clearable
|
||||
@change="searchList()"
|
||||
suffix-icon="iconfont iconSearch"/>
|
||||
</template>
|
||||
</ai-search-bar>
|
||||
<ai-table :tableData="adminList" :colConfigs="colConfigs" :total="total" :current.sync="page.pageNum"
|
||||
:size.sync="page.pageSize"
|
||||
@getList="getTableData" :col-configs="colConfigs" :dict="dict"
|
||||
@selection-change="v=>multipleSelection=v">
|
||||
<el-table-column label="角色用户" slot="users" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip
|
||||
effect="light"
|
||||
placement="top"
|
||||
:disabled="scope.row.users.length <= 2"
|
||||
content="更多角色用户请点击详情按钮">
|
||||
<span v-if="scope.row.users.length">
|
||||
{{
|
||||
scope.row.users
|
||||
.slice(0, 2)
|
||||
.map((e) => e.name + "(" + e.phone + ")")
|
||||
.join(";")
|
||||
}}
|
||||
<span v-if="scope.row.users.length > 2">...</span>
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column slot="options" label="操作" fixed="right" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-button type="text" @click="beforeCopy(row)" v-if="$permissions('admin_sysapprole_edit')">复制
|
||||
</el-button>
|
||||
<el-button type="text" @click="viewApp(row)" v-if="$permissions('admin_sysapprole_detail')">详情
|
||||
</el-button>
|
||||
<el-button type="text" @click="openRightsGraph(row)" v-if="$permissions('admin_sysapprole_detail')">关系图
|
||||
</el-button>
|
||||
<el-button type="text" @click="toAddAppRole(row)" v-if="$permissions('admin_sysapprole_edit')">编辑
|
||||
</el-button>
|
||||
<el-button type="text" @click="deleteApp(row)" v-if="$permissions('admin_sysapprole_del')">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ai-table>
|
||||
<ai-dialog
|
||||
title="应用角色详情"
|
||||
:visible.sync="viewShow"
|
||||
width="600px"
|
||||
customFooter>
|
||||
<ai-card title="基本信息">
|
||||
<template #content>
|
||||
<ai-wrapper>
|
||||
<ai-info-item label="应用角色名称" :value="viewInfo.name" isLine/>
|
||||
</ai-wrapper>
|
||||
</template>
|
||||
</ai-card>
|
||||
<ai-card title="权限信息">
|
||||
<template #content>
|
||||
<div style="margin-bottom: 16px" v-text="roleList.map(e => e.name).join('、')"/>
|
||||
</template>
|
||||
</ai-card>
|
||||
<ai-card title="角色账号">
|
||||
<template #right>
|
||||
<span style="text-align: right; color: #999">
|
||||
共<span style="color: #26f" v-text="userList.length"/>个账号
|
||||
</span>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="datail-table-body" v-if="userList.length">
|
||||
<div class="datail-item" v-for="(item, index) in userList" :key="index">
|
||||
<span class="item-name">{{ item.name }}</span>
|
||||
<span style="color: #999">{{ item.phone }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ai-card>
|
||||
<template #footer>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="toAddAppRole(viewInfo)"
|
||||
v-if="$permissions('admin_sysapprole_edit')"
|
||||
>编辑角色
|
||||
</el-button>
|
||||
</template>
|
||||
</ai-dialog>
|
||||
<ai-dialog title="权限关系图" :visible.sync="rightsGraph" class="rightsGraphDialog" customFooter>
|
||||
<rights-graph :instance="instance" :dict="dict" :app="selectApp"/>
|
||||
<el-button slot="footer" @click="rightsGraph=false">关闭</el-button>
|
||||
</ai-dialog>
|
||||
<!--复制角色-->
|
||||
<el-dialog
|
||||
class="editStyle"
|
||||
:visible.sync="copyDialog"
|
||||
width="520px"
|
||||
@close="dataInit()"
|
||||
title="复制角色">
|
||||
<el-form :model="form" label-width="80px">
|
||||
<el-form-item label="角色名" :rules="[{ required: true, message: '', trigger: 'blur' }]">
|
||||
<el-input
|
||||
v-model="editName"
|
||||
placeholder="请输入..."
|
||||
size="small"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" style="text-align: center">
|
||||
<el-button
|
||||
style="width: 92px"
|
||||
size="small"
|
||||
@click="copyDialog = false"
|
||||
>取消
|
||||
</el-button
|
||||
>
|
||||
<el-button
|
||||
style="width: 92px"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="copyFn()"
|
||||
:disabled="!editName"
|
||||
>
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
</ai-list>
|
||||
<rights-add v-else :instance="instance" :dict="dict" :permissions="permissions"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RightsAdd from "./rightsAdd";
|
||||
import RightsGraph from "./rightsGraph";
|
||||
|
||||
export default {
|
||||
name: "AppRoleRightsManager",
|
||||
components: {RightsGraph, RightsAdd},
|
||||
label: "角色管理",
|
||||
provide() {
|
||||
return {
|
||||
top: this
|
||||
}
|
||||
},
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function,
|
||||
actions: {
|
||||
default: () => ({
|
||||
list: '/admin/role/page',
|
||||
apps: '/admin/role/list-all',
|
||||
delete: '/admin/role/del',
|
||||
detail: '/admin/role/queryById-checked',
|
||||
modify: '/admin/role/modify',
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
colConfigs() {
|
||||
return [
|
||||
{type: "selection"},
|
||||
{label: "角色名", prop: "name", width: '100px'},
|
||||
{label: "所属端", prop: "type", width: '100px', dict: "roleType"},
|
||||
{label: "用户数量", prop: "roleCount", align: 'center', width: '80px'},
|
||||
{slot: "users"},
|
||||
{slot: "options"}
|
||||
]
|
||||
},
|
||||
showDetail() {
|
||||
return this.$route.hash == "#add"
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
page: {pageNum: 1, pageSize: 10},
|
||||
search: {roleName: ''},
|
||||
adminList: [], //列表数据
|
||||
total: 0,
|
||||
multipleSelection: [],
|
||||
delShow: false,
|
||||
delParams: "",
|
||||
delIds: [],
|
||||
viewShow: false,
|
||||
viewInfo: {},
|
||||
roleList: [], //详情权限列表
|
||||
row: {},
|
||||
copyDialog: false,
|
||||
titleDel: "",
|
||||
form: {},
|
||||
editName: "",
|
||||
userList: [],
|
||||
rightsGraph: false,
|
||||
selectApp: {}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getTableData();
|
||||
this.dict.load('roleType')
|
||||
},
|
||||
methods: {
|
||||
//查询table列表
|
||||
getTableData() {
|
||||
this.adminList = [];
|
||||
this.instance.post(this.actions.list, null, {
|
||||
params: {...this.page, ...this.search}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.total = res.data.total;
|
||||
this.adminList = res.data.records;
|
||||
}
|
||||
})
|
||||
},
|
||||
//查询
|
||||
searchList() {
|
||||
this.page.pageNum = 1;
|
||||
this.getTableData();
|
||||
},
|
||||
//添加按钮
|
||||
toAddAppRole(item) {
|
||||
this.$router.push({
|
||||
hash: "#add",
|
||||
query: {
|
||||
id: item.id,
|
||||
name: item.name
|
||||
},
|
||||
});
|
||||
},
|
||||
//删除
|
||||
deleteApp(e) {
|
||||
if (e == "all") {
|
||||
this.multipleSelection.map((item) => {
|
||||
this.delIds.push(item.id);
|
||||
});
|
||||
this.delParams = `ids=${this.delIds}`;
|
||||
this.titleDel = "确定要执行删除操作吗?";
|
||||
} else {
|
||||
this.delParams = `ids=${e.id}`;
|
||||
this.titleDel = "确定需要删除该角色吗?";
|
||||
}
|
||||
this.$confirm(this.titleDel, {
|
||||
type: "error",
|
||||
}).then(() => {
|
||||
this.instance.post(`${this.actions.delete}?${this.delParams}`).then(res => {
|
||||
if (res?.msg == "success") {
|
||||
this.getTableData();
|
||||
} else {
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
});
|
||||
}).catch(() => 0);
|
||||
},
|
||||
//查看信息
|
||||
viewApp(e) {
|
||||
this.userList = e.users;
|
||||
this.viewInfo = e;
|
||||
this.viewShow = true;
|
||||
this.getRowInfo(this.viewInfo.appId, this.viewInfo.id);
|
||||
},
|
||||
//查询 row 信息
|
||||
getRowInfo(appId, id) {
|
||||
this.roleList = [];
|
||||
this.instance.post(`${this.actions.detail}?id=${appId}&roleId=${id}`)
|
||||
.then(res => {
|
||||
if (res?.data) {
|
||||
this.roleList = res.data.filter(e => e.checked)
|
||||
}
|
||||
})
|
||||
},
|
||||
//复制
|
||||
beforeCopy(row) {
|
||||
this.row = row;
|
||||
this.copyDialog = true;
|
||||
this.getRowInfo(this.row.appId, this.row.id);
|
||||
},
|
||||
//确认复制
|
||||
copyFn() {
|
||||
let crr = [];
|
||||
let appRoleList = this.roleList;
|
||||
for (let i = 0; i < appRoleList.length; i++) {
|
||||
if (appRoleList[i].checked) {
|
||||
crr.push(appRoleList[i].id);
|
||||
if (appRoleList[i].list.length) {
|
||||
for (let j = 0; j < appRoleList[i].list.length; j++) {
|
||||
if (appRoleList[i].list[j].checked) {
|
||||
crr.push(appRoleList[i].list[j].id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.instance.post(`${this.actions.modify}?menus=${crr}`, null, {
|
||||
params: {
|
||||
roleName: this.editName,
|
||||
appId: this.row.appId,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.$message({message: "复制成功", type: "success"});
|
||||
this.copyDialog = false;
|
||||
this.searchList()
|
||||
}
|
||||
});
|
||||
},
|
||||
dataInit() {
|
||||
this.multipleSelection = [];
|
||||
this.row = {};
|
||||
},
|
||||
openRightsGraph(row) {
|
||||
this.rightsGraph = true
|
||||
this.selectApp = row
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AppRoleRightsManager {
|
||||
height: 100%;
|
||||
|
||||
|
||||
:deep( .ai-dialog ) {
|
||||
.ai-card {
|
||||
box-shadow: none;
|
||||
border: 1px solid #eee;
|
||||
|
||||
.aibar {
|
||||
height: 40px;
|
||||
background: #f3f6f9;
|
||||
}
|
||||
|
||||
.ai-card__body {
|
||||
padding: 0 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep( .rightsGraphDialog ) {
|
||||
.el-dialog__body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ai-dialog__content {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
:deep( .datail-table-body ) {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.datail-item {
|
||||
flex-shrink: 0;
|
||||
width: 50%;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
width: 102px;
|
||||
padding-left: 16px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.datail-item:nth-of-type(2n - 1) {
|
||||
border-right: 1px solid rgba(208, 212, 220, 1);
|
||||
width: calc(50% - 1px);
|
||||
}
|
||||
}
|
||||
|
||||
.padd-l0 {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.pad-l16 {
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</style>
|
||||
196
project/xumu/AppRoleRightsManager/rightsAdd.vue
Normal file
196
project/xumu/AppRoleRightsManager/rightsAdd.vue
Normal file
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<ai-detail class="rightsAdd">
|
||||
<ai-title :title="addTitle" slot="title" isShowBottomBorder isShowBack @onBackClick="back"/>
|
||||
<template #content>
|
||||
<el-form size="small" ref="rightsForm" :model="form" label-width="120px" :rules="rules">
|
||||
<ai-card title="基本信息">
|
||||
<template #content>
|
||||
<div class="grid">
|
||||
<el-form-item label="应用角色名称" prop="roleName">
|
||||
<el-input v-model="form.roleName" placeholder="请输入应用角色名称" clearable/>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色标记" prop="type">
|
||||
<ai-select v-model="form.type" dict="roleType" placeholder="请选择角色标记" clearable/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
</ai-card>
|
||||
<ai-card title="权限信息">
|
||||
<template #content>
|
||||
<el-form-item label="权限列表" prop="menus">
|
||||
<div class="roleList">
|
||||
<el-input v-model="filterText" placeholder="请输入..." clearable suffix-icon="iconfont iconSearch"
|
||||
@change="$refs.tree.filter(filterText)" :validate-event="false"/>
|
||||
<div class="tree_list">
|
||||
<el-tree class="filter-tree" ref="roleTree"
|
||||
:data="roleList"
|
||||
show-checkbox
|
||||
:props="defaultProps"
|
||||
default-expand-all
|
||||
:check-strictly="false"
|
||||
node-key="id"
|
||||
:default-checked-keys="form.menus"
|
||||
:filter-node-method="filterNode"
|
||||
@check="handleMenusSelect"/>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</ai-card>
|
||||
</el-form>
|
||||
</template>
|
||||
<template #footer>
|
||||
<el-button @click="back()">取消</el-button>
|
||||
<el-button type="primary" @click="confirm">保存</el-button>
|
||||
</template>
|
||||
</ai-detail>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "rightsAdd",
|
||||
inject: ['top'],
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
permissions: Function
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
roleName: '',
|
||||
id: '',
|
||||
appList: [],
|
||||
roleList: [],
|
||||
defaultProps: {
|
||||
children: 'list',
|
||||
label: 'name'
|
||||
},
|
||||
treeList: [],
|
||||
filterText: '',
|
||||
msgTitle: '添加'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.isEdit) {
|
||||
let {id, name: roleName} = this.$route.query
|
||||
this.form = {menus: [], id, roleName}
|
||||
this.msgTitle = '编辑'
|
||||
}
|
||||
this.getPermissions()
|
||||
},
|
||||
computed: {
|
||||
isEdit() {
|
||||
return this.$route.query.id
|
||||
},
|
||||
addTitle() {
|
||||
return this.isEdit ? '编辑应用角色' : '新增应用角色'
|
||||
},
|
||||
rules() {
|
||||
return {
|
||||
roleName: {required: true, message: '请输入应用角色名称'},
|
||||
roleType: {required: true, message: '请选择角色标记'},
|
||||
menus: {required: true, message: '请选择权限列表内容'},
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
filterNode(value, data) {
|
||||
if (!value) return true;
|
||||
return data.name.indexOf(value) !== -1;
|
||||
},
|
||||
//应用名称选择 获取权限列表
|
||||
getId(data) {
|
||||
if (data.list.length) {
|
||||
data.list.forEach(item => {
|
||||
this.getId(item)
|
||||
})
|
||||
} else {
|
||||
if (data.checked) {
|
||||
this.form.menus?.push(data.id)
|
||||
}
|
||||
}
|
||||
},
|
||||
getPermissions() {
|
||||
this.filterText = ''
|
||||
let {id: roleId} = this.form
|
||||
this.instance.post(this.top.actions.detail, null, {
|
||||
params: {roleId}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
this.roleList = res.data;
|
||||
if (this.isEdit) {
|
||||
this.roleList.forEach(e => this.getId(e))
|
||||
}
|
||||
this.roleList = this.roleList.filter(item => !(item.component && item.isApp == 0 && item.isMenu == 0))
|
||||
}
|
||||
})
|
||||
},
|
||||
handleMenusSelect(node, selected) {
|
||||
this.$set(this.form, 'menus', [...selected?.checkedKeys])
|
||||
this.$refs.rightsForm.validateField('menus')
|
||||
},
|
||||
//保存提交
|
||||
confirm() {
|
||||
this.$refs.rightsForm.validate(v => {
|
||||
if (v) {
|
||||
let menus = [this.$refs.roleTree?.getHalfCheckedKeys(), this.$refs.roleTree?.getCheckedKeys()]?.flat()?.toString()
|
||||
this.instance.post(this.top.actions.modify, null, {
|
||||
params: {...this.form, menus}
|
||||
}).then(res => {
|
||||
if (res?.msg == "success") {
|
||||
this.$message.success(`${this.msgTitle}应用角色成功`)
|
||||
this.back()
|
||||
this.top.searchList()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
//取消 返回
|
||||
back() {
|
||||
this.$router.push({})
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.rightsAdd {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
.el-form-item {
|
||||
.el-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.is-error {
|
||||
.roleList {
|
||||
border-color: #f46;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.roleList {
|
||||
background-color: #fcfcfc;
|
||||
border-radius: 2px;
|
||||
border: solid 1px #d0d4dc;
|
||||
padding: 8px;
|
||||
|
||||
.input {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 5px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tree_list {
|
||||
padding: 5px;
|
||||
height: 370px;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
192
project/xumu/AppRoleRightsManager/rightsGraph.vue
Normal file
192
project/xumu/AppRoleRightsManager/rightsGraph.vue
Normal file
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<section class="rightsGraph">
|
||||
<div id="RightGraph"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
;
|
||||
|
||||
export default {
|
||||
name: "rightsGraph",
|
||||
inject: ['top'],
|
||||
props: {
|
||||
instance: Function,
|
||||
dict: Object,
|
||||
app: Object
|
||||
},
|
||||
computed: {
|
||||
graphData() {
|
||||
let data = [...this.users, ...this.nodes].map(e => {
|
||||
if (e.x) {
|
||||
return e
|
||||
} else return {...e, ...this.renderPosition(e)}
|
||||
})
|
||||
return [
|
||||
{
|
||||
data,
|
||||
links: this.links,
|
||||
categories: data.map(e => e.category).flat().map(name => ({name}))
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
graph: null,
|
||||
nodes: [],
|
||||
links: [],
|
||||
users: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
graphData: {
|
||||
deep: true, handler() {
|
||||
this.refreshGraph()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initGraph() {
|
||||
let dom = document.querySelector("#RightGraph")
|
||||
if (dom) {
|
||||
this.graph = echarts.init(dom)
|
||||
this.graph.setOption({
|
||||
tooltip: {},
|
||||
series: [
|
||||
{
|
||||
type: 'graph',
|
||||
layout: 'none',
|
||||
roam: true,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'right',
|
||||
formatter: '{b}'
|
||||
},
|
||||
labelLayout: {
|
||||
hideOverlap: true,
|
||||
},
|
||||
scaleLimit: {
|
||||
min: 0.4,
|
||||
max: 4
|
||||
},
|
||||
lineStyle: {
|
||||
color: 'target',
|
||||
curveness: 0.1
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'adjacency',
|
||||
lineStyle: {
|
||||
width: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
this.graph.on('click', this.handleNodeClick)
|
||||
}
|
||||
},
|
||||
refreshGraph() {
|
||||
this.graph?.setOption({
|
||||
series: this.graphData
|
||||
})
|
||||
},
|
||||
getAppRoles(role) {
|
||||
if (role) {
|
||||
this.nodes.push({...role, category: '应用角色', value: "应用角色", symbolSize: 15})
|
||||
} else this.instance.post(this.top.actions.list, null, {
|
||||
params: {pageSize: 999}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
res.data.records.map(e => {
|
||||
this.getUsers(e.users, e.id)
|
||||
this.nodes.push({...e, category: '应用角色', value: "应用角色"})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
getUsers(pending, source) {
|
||||
pending?.map(e => {
|
||||
if (!this.users.some(u => u.id == e.phone)) {
|
||||
this.users.push({id: e.phone, name: e.name, symbolSize: 5, category: '用户', value: "用户"})
|
||||
}
|
||||
this.links.push({source, target: e.phone})
|
||||
})
|
||||
},
|
||||
getPermissions({id: roleId, name: category, dataIndex}) {
|
||||
const addNodes = (list, source, category, pos) => {
|
||||
list?.map(e => {
|
||||
let node = {
|
||||
...e,
|
||||
symbolSize: 5,
|
||||
category,
|
||||
value: e.list?.length > 0 ? "应用" : "权限",
|
||||
}
|
||||
node = {...node, ...this.renderPosition(pos || node)}
|
||||
this.nodes.splice(dataIndex, 0, node)
|
||||
if (e.checked == 1) {
|
||||
this.links.push({source, target: e.id})
|
||||
}
|
||||
addNodes(e.list, e.id, e.label, node)
|
||||
})
|
||||
}
|
||||
roleId && this.instance.post(this.top.actions.detail, null, {
|
||||
params: {roleId}
|
||||
}).then(res => {
|
||||
if (res?.data) {
|
||||
addNodes(res.data, roleId, category)
|
||||
}
|
||||
})
|
||||
},
|
||||
handleNodeClick(v) {
|
||||
let {data: role, dataIndex} = v
|
||||
if (!this.nodes.some(e => e.category == role?.name)) {
|
||||
role && this.getPermissions({...role, dataIndex})
|
||||
}
|
||||
},
|
||||
renderPosition(node) {
|
||||
node = JSON.parse(JSON.stringify(node))
|
||||
let pos = {x: 0, y: 0}
|
||||
if (node?.x) {
|
||||
pos.x = Math.max(this.graph?.getWidth() / 3 * 2, node.x) + 100 + Math.random() * 50
|
||||
pos.y = node.y + Math.random() * 100 - 50
|
||||
} else if (node.value == '应用角色') {
|
||||
pos.x = this.graph?.getWidth() / 3 - 200 + Math.random() * 200
|
||||
pos.y = this.graph?.getHeight() / 2 - 100 + Math.random() * 200
|
||||
} else if (node.value == '应用') {
|
||||
pos.x = this.graph?.getWidth() / 3 * 2 - 100 + Math.random() * 100
|
||||
pos.y = Math.random() * this.graph?.getHeight()
|
||||
} else if (node.value == '用户') {
|
||||
pos.x = Math.random() * 50
|
||||
pos.y = Math.random() * this.graph?.getHeight()
|
||||
} else {
|
||||
pos.x = this.graph?.getWidth() - 100 + Math.random() * 100
|
||||
pos.y = Math.random() * this.graph?.getHeight()
|
||||
}
|
||||
return pos
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getAppRoles(this.app)
|
||||
this.getUsers(this.app.users, this.app.id)
|
||||
this.getPermissions(this.app)
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.initGraph()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rightsGraph {
|
||||
height: 100%;
|
||||
|
||||
:deep( #RightGraph ){
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 500px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
229
project/xumu/AppSign/AppSign.vue
Normal file
229
project/xumu/AppSign/AppSign.vue
Normal file
@@ -0,0 +1,229 @@
|
||||
<template>
|
||||
<section class="AppSign">
|
||||
<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-if="logo.icon" 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>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="projectName mar-b48" :title="system.fullTitle">{{ system.fullTitle }}</div>
|
||||
<el-card class="signBox">
|
||||
<div class="choosePlatform flex column" v-if="!isAdmin&&!form.type">
|
||||
<div class="font-20 mar-b40 t-center t-bold">请选择业务端后登陆</div>
|
||||
<div class="selectPlatform fill">
|
||||
<div class="flex center pointer" v-for="op in platforms" :key="op.dictValue"
|
||||
v-text="op.dictName" @click="$set(form,'type',op.dictValue)"/>
|
||||
</div>
|
||||
<div class="mar-t32 font-12" style="align-self: flex-end">
|
||||
未注册用户请扫码添加客服咨询
|
||||
<i class="iconfont iconEwm" style="font-size: 20px"/>
|
||||
</div>
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="font-20 mar-b40 t-center t-bold"><i v-if="!isAdmin" class="el-icon-back" @click="form.type=null"/>账号登录</div>
|
||||
<el-form :model="form" ref="form" :rules="rules">
|
||||
<el-form-item prop="username">
|
||||
<el-input v-model="form.username" placeholder="请输入您的账号"/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input type="password" v-model="form.password" placeholder="请输入您的密码" show-password/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="t-right font-12">忘记密码请联系客服处理</div>
|
||||
<el-button type="primary" class="login-btn" @click="handleSignIn">登录</el-button>
|
||||
</template>
|
||||
</el-card>
|
||||
<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>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapMutations, mapState} from 'vuex'
|
||||
|
||||
const rules = {
|
||||
username: [{required: true, message: '请输入您的账号', trigger: 'blur'}],
|
||||
password: [{required: true, message: '请输入您的密码', trigger: 'blur'}]
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "AppSign",
|
||||
label: "登录页",
|
||||
data() {
|
||||
return {
|
||||
rules,
|
||||
form: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['user', 'sys']),
|
||||
instance: v => v.$request,
|
||||
system: v => v.sys?.info || {
|
||||
fullTitle: '畜牧养殖产业一体化平台'
|
||||
},
|
||||
logo: v => !!v.system.loginLogo ? {icon: v.system.loginLogo, text: v.system.loginLogoText} : {icon: v.system.logo, text: v.system.logoText},
|
||||
isAdmin: v => v.$route.hash == "#sinoecare", //用来判断是否是管理员登录,
|
||||
dict: v => v.$dict,
|
||||
platforms: v => v.dict.getDict('roleType').filter(e => !['platform', 'other', 'service'].includes(e.dictValue))
|
||||
},
|
||||
created() {
|
||||
this.dict.load("roleType")
|
||||
if (this.user.token) {
|
||||
this.handleGotoHome()
|
||||
} else {
|
||||
const {code} = this.$route.query
|
||||
if (code) {
|
||||
this.toLogin(code)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['setToken']),
|
||||
login(data) {
|
||||
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"})
|
||||
}
|
||||
},
|
||||
handleSignIn() {
|
||||
this.$refs.form.validate().then(() => {
|
||||
const password = this.$encryption(this.form.password)
|
||||
this.form.type = this.form.type || "platform"
|
||||
this.$request.post("/api/oauth/token", null, {
|
||||
auth: {username: 'villcloud', password: "villcloud"},
|
||||
params: {grant_type: 'password', scope: 'server', ...this.form, password}
|
||||
}).then(data => {
|
||||
this.login(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AppSign {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
|
||||
|
||||
.selectPlatform {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-gap: 10px;
|
||||
|
||||
& > div {
|
||||
color: #fff;
|
||||
background: $primaryBtnColor;
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.signBox {
|
||||
width: 500px;
|
||||
min-height: 300px;
|
||||
position: relative;
|
||||
color: $primaryColor;
|
||||
|
||||
.choosePlatform {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: inherit;
|
||||
}
|
||||
|
||||
.el-icon-back {
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
top: 25px;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
margin: 16px auto;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.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-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>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -4,7 +4,6 @@
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico"/>
|
||||
<link rel="stylesheet" href="<%= BASE_URL %>cdn/viewerjs/1.11.6/viewer.css"/>
|
||||
<link rel="stylesheet" href="<%= BASE_URL %>cdn/avue/2.10.18/index.css">
|
||||
<link rel="stylesheet" href="<%= BASE_URL %>cdn/jsoneditor/10.0.2/jsoneditor.min.css">
|
||||
|
||||
@@ -78,7 +78,7 @@ export default {
|
||||
})
|
||||
break;
|
||||
case 'user':
|
||||
this.$router.push({name: "个人中心"})
|
||||
this.$router.push({name: "AppUserInfo"})
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<section class="mainContent">
|
||||
<ai-nav-tab :fixed="homePage" :routes="routes"/>
|
||||
<ai-nav-tab :fixed="$HomePage" :routes="routes"/>
|
||||
<router-view v-if="refresh"/>
|
||||
</section>
|
||||
</template>
|
||||
@@ -11,8 +11,8 @@ 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}))
|
||||
...mapState(['user']),
|
||||
routes: v => v.user.info?.menuSet?.map(e => ({...e, label: e.name, name: e.component}))
|
||||
},
|
||||
watch: {
|
||||
$route(v, old) {
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
|
||||
<script>
|
||||
import {mapGetters} from "vuex";
|
||||
import qs from "querystring";
|
||||
|
||||
export default {
|
||||
name: "sliderNav",
|
||||
@@ -40,11 +39,12 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['mods']),
|
||||
navs: v => v.sortList(v.menuList),
|
||||
navs: v => [v.$HomePage, ...v.sortList(v.menuList)],
|
||||
menuPath() {
|
||||
let paths = [], current = this.mods?.find(e => e.route == this.$route.name)
|
||||
const mods = [this.$HomePage, ...this.mods]
|
||||
let paths = [], current = mods?.find(e => e.component == this.$route.name)
|
||||
const findParent = id => {
|
||||
let menu = this.mods?.find(e => e.id == id)
|
||||
let menu = mods?.find(e => e.id == id)
|
||||
if (menu) {
|
||||
paths.push(menu.id)
|
||||
if (!!menu.parentId) findParent(menu.parentId)
|
||||
@@ -80,12 +80,11 @@ export default {
|
||||
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])})
|
||||
let {component, path} = item, urlParams = ''
|
||||
if (path.indexOf('?') > -1) {
|
||||
urlParams = path.split('?').at(-1)
|
||||
}
|
||||
this.goto({path: `/v/${[component, urlParams].filter(Boolean).join("?")}`})
|
||||
}
|
||||
},
|
||||
goto(item) {
|
||||
@@ -109,7 +108,6 @@ export default {
|
||||
}
|
||||
}
|
||||
list.forEach(e => findParent(e))
|
||||
console.log(map, list)
|
||||
this.initMenu(Object.values(map))
|
||||
} else {
|
||||
this.initMenu()
|
||||
@@ -219,7 +217,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ai-menu ){
|
||||
:deep(.ai-menu ) {
|
||||
padding-left: 0;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
@@ -233,7 +231,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.searchApp ){
|
||||
:deep(.searchApp ) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 44px;
|
||||
|
||||
@@ -6,7 +6,6 @@ import utils from './utils';
|
||||
import vcUI from 'dui/packages';
|
||||
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";
|
||||
//备注底座信息,勿删
|
||||
@@ -18,8 +17,10 @@ Vue.use(appComp);
|
||||
Vue.config.productionTip = false;
|
||||
Vue.prototype.$cdn = "https://cdn.cunwuyun.cn"
|
||||
Vue.prototype.$request = axios
|
||||
const home = extra.homePage || 'console'
|
||||
Vue.prototype.$HomePage = {name: '工作台', label: '工作台', component: home, id: `/v/${home}`, path: `/v/${home}`, style: "iconfont iconNav_Dashborad"}
|
||||
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"))
|
||||
const loadPage = () => 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)
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ 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/")
|
||||
config.url = config.url.replace(/^\/(app|auth|admin)\//, "/api/")
|
||||
}
|
||||
if (config.url.startsWith("/node")) {
|
||||
config.baseURL = "/ns"
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import {routes} from "./autoRoutes"
|
||||
import routes from "./apps.js"
|
||||
import config from "../config.json"
|
||||
import store from "@/utils/store";
|
||||
|
||||
const {state: {user}, commit, dispatch} = store
|
||||
const signOut = () => commit("signOut")
|
||||
Vue.use(VueRouter)
|
||||
export default new VueRouter({
|
||||
const router = new VueRouter({
|
||||
base: config.base || '/',
|
||||
mode: 'history',
|
||||
hashbang: false,
|
||||
@@ -17,3 +20,31 @@ export default new VueRouter({
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
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 (to.name == "Home" && user.token && !user.info?.id) {
|
||||
dispatch("getUserInfo").then(() => next())
|
||||
} else if (user.token) {
|
||||
to.name == "init" ? next({name: "Home"}) : next()
|
||||
} else {
|
||||
signOut()
|
||||
}
|
||||
})
|
||||
router.onError(err => {
|
||||
console.error(err)
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
@@ -9,12 +9,8 @@ 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) {
|
||||
@@ -35,7 +31,6 @@ export default new Vuex.Store({
|
||||
getters: {
|
||||
//后台数据库中的应用集合,在本工程中不一定存在
|
||||
mods: state => [
|
||||
state.homePage,
|
||||
state.user.info?.menuSet?.map(e => ({route: e.id, ...e, label: e.name}))
|
||||
].flat().filter(Boolean)
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<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"/>
|
||||
<router-view v-else :instance="$request" :dict="$dict" :permissions="$permissions" :menuName="currentApp.name"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@ import CryptoJs from "crypto-js";
|
||||
export const $encryption = (params, c = 0) => {
|
||||
if (CryptoJs) {
|
||||
const key = "thanks,villcloud"
|
||||
let password = typeof params == "object" ? params.password : params
|
||||
let iv = CryptoJs.enc.Latin1.parse(key)
|
||||
let encrypted = CryptoJs.AES.encrypt(params.password, iv, {
|
||||
let encrypted = CryptoJs.AES.encrypt(password, iv, {
|
||||
iv,
|
||||
mode: CryptoJs.mode.CBC,
|
||||
padding: CryptoJs.pad.ZeroPadding
|
||||
|
||||
@@ -80,12 +80,24 @@ $--font-path: '~element-ui/lib/theme-chalk/fonts';
|
||||
/**
|
||||
不换行文本
|
||||
*/
|
||||
.nowrap-text {
|
||||
.nowrap-text, .t-nowrap {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.t-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.t-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.t-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/**
|
||||
表头式样
|
||||
*/
|
||||
@@ -348,11 +360,15 @@ div[flex], .flex {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
@for $i from 2 through 5 {
|
||||
@for $i from 2 through 10 {
|
||||
&.c-#{$i} {
|
||||
grid-template-columns: repeat($i, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
grid-column: 1/-1;
|
||||
}
|
||||
}
|
||||
|
||||
// 2.0公共样式
|
||||
@@ -388,8 +404,10 @@ div[flex], .flex {
|
||||
}
|
||||
|
||||
.el-message-box__content {
|
||||
padding-left: 77px;
|
||||
padding-top: 4px;
|
||||
min-height: 60px;
|
||||
padding-left: 40px;
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.el-message-box__title {
|
||||
@@ -410,7 +428,6 @@ div[flex], .flex {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
.el-message-box__btns {
|
||||
|
||||
@@ -14,8 +14,11 @@
|
||||
export default {
|
||||
name: "AiPullDown",
|
||||
props: {
|
||||
target: String,
|
||||
height: {default: 4},
|
||||
value: Boolean,
|
||||
},
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'expand'
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -25,32 +28,34 @@ export default {
|
||||
methods: {
|
||||
handleExpand() {
|
||||
this.expand = !this.expand
|
||||
if (this.target) {
|
||||
|
||||
} else this.$emit('change', this.expandStyle)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
btnText() {
|
||||
return this.expand ? '收起高级搜索' : '展开高级搜索'
|
||||
},
|
||||
expandStyle() {
|
||||
let initStyle = {overflow: 'hidden', height: `${this.height}px`}
|
||||
this.expand && (initStyle.height = "auto")
|
||||
return initStyle
|
||||
return this.expand ? '收起' : '展开'
|
||||
},
|
||||
expandIcon() {
|
||||
return this.expand ? 'iconfont iconDouble_Up' : 'iconfont iconDouble_Down'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$emit("change", this.expandStyle)
|
||||
watch: {
|
||||
expand: {
|
||||
immediate: true, handler(v) {
|
||||
this.$emit('expand', v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.AiPullDown {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 24px;
|
||||
transform: translateY(100%);
|
||||
background-color: #fff;
|
||||
|
||||
.line {
|
||||
flex: 1;
|
||||
|
||||
@@ -21,33 +21,17 @@
|
||||
:is="colConfig.component"
|
||||
:col-config="colConfig">
|
||||
</component>
|
||||
<el-table-column
|
||||
v-else-if="colConfig.dict"
|
||||
:key="colConfig.id"
|
||||
v-bind="colConfig">
|
||||
<span slot-scope="{row}" :style="{color:colConfig.color||dict.getColor(colConfig.dict, row[colConfig.prop])}">
|
||||
{{ dict.getLabel(colConfig.dict, row[colConfig.prop]) }}
|
||||
</span>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-else-if="colConfig.openType"
|
||||
:key="colConfig.id"
|
||||
v-bind="colConfig">
|
||||
<template v-slot="{row}">
|
||||
<ai-open-data :type="colConfig.openType" :openid="row[colConfig.prop]"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-else-if="colConfig.type"
|
||||
:key="colConfig.id"
|
||||
v-bind="colConfig"
|
||||
:width="colConfig.width || 100"/>
|
||||
<el-table-column v-else v-bind="colConfig" :key="colConfig.id"
|
||||
:show-overflow-tooltip="colConfig['show-overflow-tooltip'] != false">
|
||||
<template slot-scope="scope">
|
||||
<render-slot v-if="colConfig.render" :render="colConfig.render" :row="scope.row" :index="scope.$index"
|
||||
:column="colConfig"/>
|
||||
<span v-else>{{ getValue(colConfig, scope.row) }}</span>
|
||||
<el-table-column v-else-if="colConfig.type" v-bind="colConfig" :width="colConfig.width||80" align="center"/>
|
||||
<el-table-column v-else v-bind="colConfig">
|
||||
<template slot-scope="{row,$index}">
|
||||
<span v-if="colConfig.dict" :style="{color:colConfig.color||dict.getColor(colConfig.dict, row[colConfig.prop])}" v-text="dict.getLabel(colConfig.dict, row[colConfig.prop])"/>
|
||||
<render-slot v-else-if="colConfig.render" :render="colConfig.render" :row="row" :index="$index" :column="colConfig"/>
|
||||
<el-input v-else-if="colConfig.edit" v-model="row[colConfig.prop]" size="small" placeholder="请输入" clearable/>
|
||||
<el-input v-else-if="colConfig.num" v-model.number="row[colConfig.prop]" size="small" placeholder="请输入" clearable/>
|
||||
<ai-select v-else-if="colConfig.select" v-model="row[colConfig.prop]" v-bind="colConfig.select"/>
|
||||
<ai-uploader v-else-if="colConfig.upload" v-model="row[colConfig.prop]" :limit="1" v-bind="colConfig.upload"/>
|
||||
<ai-open-data v-else-if="colConfig.openType" :type="colConfig.openType" :openid="row[colConfig.prop]"/>
|
||||
<span v-else>{{ getValue(colConfig, row) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
@@ -88,6 +72,7 @@
|
||||
<script>
|
||||
import moment from 'dayjs'
|
||||
import dict from "../../lib/js/dict"
|
||||
import AiUploader from "./AiUploader.vue";
|
||||
|
||||
export default {
|
||||
name: 'AiTable',
|
||||
@@ -121,6 +106,7 @@ export default {
|
||||
}
|
||||
},
|
||||
components: {
|
||||
AiUploader,
|
||||
renderSlot: {
|
||||
functional: true,
|
||||
props: {
|
||||
@@ -178,7 +164,7 @@ export default {
|
||||
},
|
||||
getValue(colConfig, row) {
|
||||
if (typeof colConfig.format === 'function') {
|
||||
return colConfig.format.call(this, row[colConfig.prop])
|
||||
return colConfig.format.call(this, row[colConfig.prop], row)
|
||||
}
|
||||
if (colConfig.dateFormat) {
|
||||
return moment(row[colConfig.prop]).format(colConfig.dateFormat)
|
||||
@@ -283,6 +269,31 @@ export default {
|
||||
tr td:first-child .cell {
|
||||
padding-left: 40px !important;
|
||||
}
|
||||
|
||||
.uploader {
|
||||
.el-upload-list__item {
|
||||
width: 100px;
|
||||
height: 32px;
|
||||
margin: unset;
|
||||
|
||||
& > img {
|
||||
width: unset;
|
||||
margin-left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.uploaderBox {
|
||||
width: 100px !important;
|
||||
height: 32px !important;
|
||||
flex-direction: row !important;
|
||||
gap: 4px;
|
||||
|
||||
.iconfont {
|
||||
font-size: 24px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep( .el-table__fixed-right ) {
|
||||
|
||||
@@ -38,13 +38,11 @@
|
||||
</section>
|
||||
</template>
|
||||
<script>
|
||||
import AiHighlight from "../layout/AiHighlight";
|
||||
import instance from "../../lib/js/request";
|
||||
import Area from "../../lib/js/area";
|
||||
import instance from "../../../lib/js/request";
|
||||
import Area from "../../../lib/js/area";
|
||||
|
||||
export default {
|
||||
name: 'AiArea',
|
||||
components: {AiHighlight},
|
||||
inject: {
|
||||
elFormItem: {default: ""},
|
||||
elForm: {default: ''},
|
||||
67
ui/packages/common/area/AiAreaTree.vue
Normal file
67
ui/packages/common/area/AiAreaTree.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<script>
|
||||
import instance from "dui/lib/js/request";
|
||||
|
||||
export default {
|
||||
name: "AiAreaTree",
|
||||
props: {
|
||||
instance: {default: () => instance},
|
||||
action: {default: "/admin/area/queryAreaByParentId"},
|
||||
rootId: String,
|
||||
value: String,
|
||||
range: {default: 5} //可选最小级别地区
|
||||
},
|
||||
model: {
|
||||
prop: "value",
|
||||
event: "input"
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
areaProps: {
|
||||
label: "name",
|
||||
isLeaf: "isLeaf"
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getAreas(node, resolve) {
|
||||
let id = node.data?.id
|
||||
if (node.level == 0) {
|
||||
id = this.rootId
|
||||
}
|
||||
this.getAreasByParent(id).then(resolve)
|
||||
},
|
||||
handleClick(data) {
|
||||
this.$emit("input", data.id)
|
||||
},
|
||||
getAreasByParent(id) {
|
||||
return this.instance.post(this.action, null, {
|
||||
withoutToken: !0,
|
||||
params: {id}
|
||||
}).then(res => {
|
||||
return res.data?.map(e => ({...e, isLeaf: e.type == this.range})) || []
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-scrollbar class="AiAreaTree">
|
||||
<el-tree :load="getAreas" @node-click="handleClick" lazy :props="areaProps"/>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.AiAreaTree {
|
||||
min-width: 200px;
|
||||
height: 100%;
|
||||
|
||||
:deep(.el-scrollbar__wrap) {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
:deep(.el-tree) {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
108
ui/packages/layout/AiPage.vue
Normal file
108
ui/packages/layout/AiPage.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<script>
|
||||
import AiTitle from "../basic/AiTitle.vue";
|
||||
|
||||
export default {
|
||||
name: "AiPage",
|
||||
components: {AiTitle},
|
||||
props: {
|
||||
contentString: {default: "card"},
|
||||
showBack: Boolean
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="AiPage">
|
||||
<slot v-if="$slots.title" name="title"/>
|
||||
<ai-title v-else-if="$attrs.title" :title="$attrs.title" isShowBottomBorder :isShowBack="showBack" @onBackClick="$router.push({})"/>
|
||||
<div class="fill main">
|
||||
<div v-if="$slots.left" class="left">
|
||||
<slot name="left"/>
|
||||
</div>
|
||||
<el-scrollbar class="fill" :class="[contentString,$slots.footer?'hasFooter':'']">
|
||||
<slot/>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<div class="footer" v-if="$slots.footer">
|
||||
<slot name="footer"/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.AiPage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
overflow: hidden;
|
||||
background: #f3f6f9;
|
||||
position: relative;
|
||||
|
||||
.main {
|
||||
display: flex;
|
||||
padding: 20px 0;
|
||||
gap: 10px;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
|
||||
|
||||
:deep(.el-scrollbar__wrap) {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
& > .fill {
|
||||
&.card {
|
||||
padding: 12px 16px 12px;
|
||||
background: #FFFFFF;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 4px 6px -2px rgba(15, 15, 21, 0.15);
|
||||
}
|
||||
|
||||
&.detail {
|
||||
margin-left: 50%;
|
||||
transform: translateX(-50%);
|
||||
max-width: 1200px;
|
||||
overflow-y: auto;
|
||||
|
||||
}
|
||||
|
||||
&.blank {
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&.hasFooter {
|
||||
padding-bottom: 64px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 64px;
|
||||
background: #F5F5F5;
|
||||
box-shadow: 0 1px 0 0 #E5E5E5;
|
||||
|
||||
.el-button {
|
||||
width: 92px;
|
||||
}
|
||||
}
|
||||
|
||||
.left {
|
||||
max-width: 50%;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,14 +1,12 @@
|
||||
<template>
|
||||
<section>
|
||||
<div class="AiSearchBar" :class="{bottomBorder}" :style="searchBarStyle">
|
||||
<div ref="AiSearchBarZone" class="searchLeftZone">
|
||||
<slot name="left"/>
|
||||
</div>
|
||||
<div class="searchRightZone" ref="searchRightZone">
|
||||
<slot name="right"/>
|
||||
</div>
|
||||
<section class="AiSearchBar" :class="{bottomBorder,isSingleRow,expand}">
|
||||
<div ref="AiSearchBarZone" class="searchLeftZone">
|
||||
<slot name="left"/>
|
||||
</div>
|
||||
<ai-pull-down v-if="!isSingleRow" @change="handlePullDown" :height="rightHeight"/>
|
||||
<div class="searchRightZone" ref="searchRightZone" v-if="$slots.right">
|
||||
<slot name="right"/>
|
||||
</div>
|
||||
<ai-pull-down v-if="!isSingleRow" v-model="expand"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -27,18 +25,11 @@ export default {
|
||||
height: 0,
|
||||
rightHeight: 0,
|
||||
searchBarStyle: {},
|
||||
observer: null
|
||||
observer: null,
|
||||
expand: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handlePullDown(style) {
|
||||
this.searchBarStyle = style
|
||||
if (style.height == 'auto') {
|
||||
this.searchBarStyle.marginBottom = '16px'
|
||||
} else {
|
||||
this.searchBarStyle.marginBottom = '0'
|
||||
}
|
||||
},
|
||||
initSize() {
|
||||
this.height = this.$refs?.AiSearchBarZone?.offsetHeight
|
||||
this.rightHeight = this.$refs?.searchRightZone?.offsetHeight + 12
|
||||
@@ -63,12 +54,24 @@ export default {
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding-bottom: 12px;
|
||||
padding-bottom: 36px;
|
||||
position: relative;
|
||||
height: 64px;
|
||||
overflow: hidden;
|
||||
|
||||
&.isSingleRow {
|
||||
height: 44px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
&.bottomBorder {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
&.expand {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
:deep(.searchLeftZone ) {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
@@ -76,6 +79,8 @@ export default {
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
overflow: hidden;
|
||||
//height: 40px;
|
||||
}
|
||||
|
||||
:deep(.searchRightZone ) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
const path = require('path');
|
||||
const proxy = process.env.VUE_APP_API || "http://192.168.1.87:9000"
|
||||
const port = process.env.VUE_APP_PORT || 7000
|
||||
console.log("当前模式:", process.env.NODE_ENV)
|
||||
console.log("当前后台服务地址:", proxy, "\n")
|
||||
|
||||
module.exports = {
|
||||
lintOnSave: false,
|
||||
productionSourceMap: false,
|
||||
|
||||
Reference in New Issue
Block a user