Browse Source

人脸识别

master
xuhuajiao 4 days ago
parent
commit
7db5b0a3cd
  1. 1
      package.json
  2. 14
      src/assets/iconfonts/light/iconfont.css
  3. 2
      src/assets/iconfonts/light/iconfont.js
  4. 14
      src/assets/iconfonts/light/iconfont.json
  5. BIN
      src/assets/iconfonts/light/iconfont.ttf
  6. BIN
      src/assets/iconfonts/light/iconfont.woff
  7. BIN
      src/assets/iconfonts/light/iconfont.woff2
  8. BIN
      src/assets/images/user.jpg
  9. 2
      src/views/digitalScreen/module/areaSetting.vue
  10. 16
      src/views/faceRecognition/faceRecLog.vue
  11. 31
      src/views/faceRecognition/module/batchImport.vue
  12. 151
      src/views/faceRecognition/module/faceSearch.vue
  13. 72
      src/views/faceRecognition/module/selfRegister.vue
  14. 196
      src/views/faceRecognition/personInfoManage.vue
  15. 13
      src/views/inquiryMachine/column.vue
  16. 52
      src/views/inquiryMachine/content.vue
  17. 175
      src/views/inquiryMachine/form.vue
  18. 94
      src/views/inquiryMachine/menu.vue

1
package.json

@ -74,6 +74,7 @@
"pdfdist-mergeofd": "^2.2.228",
"pdfjs-dist": "^2.12.313",
"print-js": "^1.6.0",
"qrcodejs2": "^0.0.2",
"qs": "^6.10.1",
"quill": "^1.3.7",
"quill-image-resize-module": "^3.0.0",

14
src/assets/iconfonts/light/iconfont.css

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 3966148 */
src: url('iconfont.woff2?t=1756345950057') format('woff2'),
url('iconfont.woff?t=1756345950057') format('woff'),
url('iconfont.ttf?t=1756345950057') format('truetype');
src: url('iconfont.woff2?t=1757381540478') format('woff2'),
url('iconfont.woff?t=1757381540478') format('woff'),
url('iconfont.ttf?t=1757381540478') format('truetype');
}
.iconfont {
@ -13,6 +13,14 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-yooxi:before {
content: "\e69d";
}
.icon-heimingdan:before {
content: "\e69f";
}
.icon-caidan:before {
content: "\e69c";
}

2
src/assets/iconfonts/light/iconfont.js
File diff suppressed because it is too large
View File

14
src/assets/iconfonts/light/iconfont.json

@ -5,6 +5,20 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "778898",
"name": "vip",
"font_class": "yooxi",
"unicode": "e69d",
"unicode_decimal": 59037
},
{
"icon_id": "9826652",
"name": "黑名单",
"font_class": "heimingdan",
"unicode": "e69f",
"unicode_decimal": 59039
},
{
"icon_id": "4438260",
"name": "菜单",

BIN
src/assets/iconfonts/light/iconfont.ttf

BIN
src/assets/iconfonts/light/iconfont.woff

BIN
src/assets/iconfonts/light/iconfont.woff2

BIN
src/assets/images/user.jpg

After

Width: 110  |  Height: 160  |  Size: 2.9 KiB

2
src/views/digitalScreen/module/areaSetting.vue

@ -127,7 +127,7 @@
>
{{ link }}
</a>
<i class="iconfont" :class="copied ? 'icon-fuzhichenggong1' : 'icon-fuzhi'" @click="copyLink" />
<!-- <i class="iconfont" :class="copied ? 'icon-fuzhichenggong1' : 'icon-fuzhi'" @click="copyLink" /> -->
</p>
<p style="padding: 15px 0;">2.在地图上找到并选择正确的区域区域最小单位是/</p>
<div style="display: flex; justify-content: space-between;">

16
src/views/faceRecognition/faceRecLog.vue

@ -4,9 +4,9 @@
<div class="head-container">
<div class="head-search" style="align-items: center;">
<!-- 搜索 -->
<el-select v-model="query.status" clearable size="small" placeholder="操作类型" class="filter-item" style="width: 150px;" @change="crud.toQuery">
<el-select v-model="query.status" clearable size="small" placeholder="用户类型" class="filter-item" style="width: 120px;" @change="crud.toQuery">
<i slot="prefix" class="iconfont icon-zhuangtai" />
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
<el-option v-for="item in userTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
</el-select>
<el-input
v-model="keyWord"
@ -27,6 +27,10 @@
/>
</el-select>
</el-input>
<el-select v-model="query.status" clearable size="small" placeholder="操作类型" class="filter-item" style="width: 150px;" @change="crud.toQuery">
<i slot="prefix" class="iconfont icon-zhuangtai" />
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
</el-select>
<date-range-picker v-model="blurryTime" class="date-item" />
<rrOperation />
</div>
@ -46,6 +50,7 @@
<el-table ref="table" v-loading="crud.loading" highlight-current-row style="width: 100%;" height="calc(100vh - 330px)" :data="crud.data" @selection-change="crud.selectionChangeHandler">
<el-table-column type="index" label="序号" width="55" />
<el-table-column prop="name" label="操作者(读者证号/读者姓名)" />
<el-table-column prop="name" label="用户类型" />
<el-table-column prop="code" label="操作设备(设备编号/设备名称)" />
<el-table-column prop="type" label="所属机构(机构编号/机构名称)" />
<el-table-column prop="cardNum" label="操作类型" />
@ -56,7 +61,7 @@
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
<pagination v-if="crud.data.length !== 0" />
</div>
</div>
</template>
@ -94,6 +99,11 @@ export default {
edit: ['admin', 'column:edit'],
del: ['admin', 'column:del']
},
userTypeOptions: [
{ key: '1', display_name: '普通用户' },
{ key: '2', display_name: 'VIP用户' },
{ key: '3', display_name: '黑名单用户' }
],
enabledTypeOptions: [
{ key: '1', display_name: '借书登录' },
{ key: '2', display_name: '信息绑定登录' },

31
src/views/faceRecognition/module/batchImport.vue

@ -0,0 +1,31 @@
<template>
<el-dialog :close-on-click-modal="false" :modal-append-to-body="false" append-to-body title="批量导入" :visible.sync="batchImportVisible">
<div class="setting-dialog" style="display: flex; justify-content: center; flex-direction: column; align-items: center;">
<p>注意批量导入需在指定的模板文件内完成数据录入后上传文件再由系统执行导入操作</p>
<div class="btn-wrap">
<el-button size="mini"> <i class="iconfont icon-xiazai" />下载模板</el-button>
<el-button size="mini"> <i class="iconfont icon-piliangchengjian" />导入文件</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
export default {
data() {
return {
batchImportVisible: false
}
},
mounted() {
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.btn-wrap{
padding: 30px 0 20px 0;
}
</style>

151
src/views/faceRecognition/module/faceSearch.vue

@ -0,0 +1,151 @@
<template>
<el-dialog :close-on-click-modal="false" :modal-append-to-body="false" append-to-body title="人脸查询" :visible.sync="faceSearchVisible">
<div class="setting-dialog" style="display: flex; justify-content: flex-start;">
<div class="upload-img-input">
<input ref="fileInput" type="file" @change="changeFile($event)">
<div class="upload-libImg">
<img src="~@/assets/images/user.jpg" alt="">
<span>点击上传照片进行比对</span>
</div>
</div>
<ul class="face-result-info">
<li><span>馆代码</span>ftzn</li>
<li><span>读者证号</span>78198239</li>
<li><span>姓名</span>张三</li>
<li><span>身份证号</span>420105198500000000</li>
<li><span>性别</span></li>
<li><span>联系方式</span>15800000000</li>
<li><span>类型</span>普通用户</li>
</ul>
</div>
</el-dialog>
</template>
<script>
// import { mapGetters } from 'vuex'
// import { getCurrentTime } from '@/utils/index'
import { upload } from '@/utils/upload'
export default {
data() {
return {
faceSearchVisible: false,
file: null,
fileNames: '',
fileSize: '',
filePath: '',
px: '',
imageUrl: null
}
},
mounted() {
},
methods: {
async changeFile(e) {
const file = e.target.files[0]
if (file && file.type.startsWith('image/')) {
this.file = e.target.files[0]
this.fileSize = this.file.size
this.fileNames = this.file.name
const fileBase64 = await this.getBase64(this.file)
const res = await this.getImgPx(fileBase64)
this.imageUrl = fileBase64
this.px = res.width + 'px*' + res.height + 'px'
upload(this.baseApi + '/api/fileRelevant/uploadOtherImg', this.file).then(res => {
console.log(res)
if (res.data.code === 200) {
this.filePath = res.data.data
// this.$emit('childCover', res.data.data)
}
})
} else {
this.$message({ message: '只可上传图片', type: 'error', offset: 8 })
this.imageUrl = null
}
},
// base64
getBase64(file) {
const reader = new FileReader()
reader.readAsDataURL(file)
return new Promise((resolve) => {
reader.onload = () => {
resolve(reader.result)
}
})
},
//
getImgPx(img) {
const image = new Image()
image.src = img
return new Promise((resolve) => {
image.onload = () => {
const width = image.width
const height = image.height
resolve({ width, height })
}
})
}
}
}
</script>
<style lang="scss" scoped>
.upload-img-input{
position: relative;
width: 150px;
height: 230px;
& input{
position: absolute;
left: 0;
top: 0;
width: 150px;
height: 230px;
opacity: 0;
z-index: 9999;
}
}
.upload-libImg{
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
width: 150px;
height: 230px;
font-size: 14px;
font-weight: bold;
color: #0c0e1e;
text-align: center;
overflow: hidden;
& img{
display: block;
height: 200px;
}
& span{
display: block;
margin-top: 10px;
}
}
.face-result-info{
margin-left: 20px;
padding-left: 10px;
border-left: 1px solid #EBEEF5;
display: flex;
flex-wrap: wrap;
li{
width: 50%;
line-height: 20px;
font-size: 14px;
font-weight: bold;
color: #0c0e1e;
span{
display: inline-block;
width: 70px;
text-align: right;
font-weight: normal;
color: #606266;
}
}
}
</style>

72
src/views/faceRecognition/module/selfRegister.vue

@ -0,0 +1,72 @@
<template>
<el-dialog :close-on-click-modal="false" :modal-append-to-body="false" append-to-body title="自助注册" :visible.sync="selfRegisterVisible">
<div class="setting-dialog">
<a
:href="link"
target="_blank"
rel="noopener noreferrer"
class="external-link"
>
{{ link }}
</a>
<div class="qrcode-wrapper">
<div ref="qrcode" class="qrcode" />
</div>
</div>
</el-dialog>
</template>
<script>
import Qrcode from 'qrcodejs2'
export default {
data() {
return {
selfRegisterVisible: false,
link: 'http://192.168.99.72:8080/selfhelp/initReaderCheck.do?strLibcode=FTZN'
}
},
mounted() {
},
methods: {
generateQrcode() {
const qrcodeRef = this.$refs.qrcode
if (!qrcodeRef) return
new Qrcode(qrcodeRef, {
text: this.link,
width: 200,
height: 200,
colorDark: '#333',
colorLight: '#fff',
correctLevel: Qrcode.CorrectLevel.L
})
},
open() {
this.selfRegisterVisible = true
this.$nextTick(() => this.generateQrcode())
}
}
}
</script>
<style lang="scss" scoped>
.setting-dialog{
text-align: center;
}
.external-link {
font-size: 16px;
// color: #0348f3;
text-decoration: none;
}
.external-link:hover {
text-decoration: underline;
}
.qrcode-wrapper {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
</style>

196
src/views/faceRecognition/personInfoManage.vue

@ -1,20 +1,18 @@
<template>
<div class="app-container row-container">
<!--工具栏-->
<div class="head-container">
<div class="head-search" style="align-items: center; ">
<!-- 搜索 -->
<el-select v-model="query.status" clearable size="small" placeholder="状态" class="filter-item" style="width: 120px;" @change="crud.toQuery">
<el-select v-model="query.status" clearable size="small" placeholder="用户类型" class="filter-item" style="width: 120px;" @change="crud.toQuery">
<i slot="prefix" class="iconfont icon-zhuangtai" />
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
<el-option v-for="item in userTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-input v-model="query.blurry" size="small" clearable placeholder="输入证号、姓名、身份证号搜索" prefix-icon="el-icon-search" class="filter-item" style="width: 260px;" @keyup.enter.native="crud.toQuery" />
<rrOperation />
<el-button size="mini" class="face-search" style="background: transparent; margin-right: 10px;">
<el-button size="mini" class="face-search" style="background: transparent; margin-right: 10px;" @click="handleFaceSearch">
<i class="iconfont icon-shenfen" />
人脸查询
</el-button>
<el-checkbox v-model="fiterBlackUser" label="仅显示黑名单用户" name="fiterBlackUser" @change="handleFiterBlackUser($event)" />
<el-checkbox v-model="fiterBlackUser" label="暂无照片用户" name="fiterBlackUser" @change="handleFiterBlackUser($event)" />
</div>
<crudOperation :permission="permission">
<template v-slot:middle>
@ -24,11 +22,11 @@
</el-button>
</template>
<template v-slot:right>
<el-button size="mini">
<el-button size="mini" @click="handleBatchImport">
<i class="iconfont icon-piliangchengjian" />
批量导入
</el-button>
<el-button size="mini">
<el-button size="mini" @click="handleSelfRegister">
<i class="iconfont icon-yuzhuceqiye" />
自助注册
</el-button>
@ -42,49 +40,45 @@
<span class="dialog-right-top" />
<span class="dialog-left-bottom" />
<div class="setting-dialog">
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="110px">
<el-row>
<el-form-item label="栏目名称" prop="columnName">
<el-input v-model="form.columnName" style="width: 586px;" />
<div style="display: flex; justify-content: flex-start;">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="110px">
<el-form-item label="馆代码" prop="libcode">
<el-input v-model="form.libcode" disabled placeholder="系统自动输入" />
</el-form-item>
</el-row>
<el-form-item label="栏目编码" prop="columnNo">
<el-input v-model="form.columnNo" />
</el-form-item>
<el-form-item label="栏目类型" prop="columnType">
<el-radio-group v-model="form.columnType">
<el-radio :label="1">单页展示</el-radio>
<el-radio :label="0">列表展示</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="排序" prop="columnOrders">
<el-input-number
v-model.number="form.columnOrders"
:min="0"
:max="999"
controls-position="right"
/>
</el-form-item>
<el-form-item label="内容类型" prop="columnContentType">
<el-radio-group v-model="form.columnContentType">
<el-radio :label="1">图文</el-radio>
<el-radio :label="0">链接</el-radio>
</el-radio-group>
</el-form-item>
<el-row>
<el-form-item label="状态" prop="columnStatus">
<el-radio-group v-model="form.columnStatus">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">停用</el-radio>
<el-form-item label="读者证号" prop="code">
<el-input v-model="form.code" placeholder="请输入" />
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name" placeholder="请输入" />
</el-form-item>
<el-form-item label="身份证号" prop="card">
<el-input v-model="form.card" placeholder="请输入" />
</el-form-item>
<el-form-item label="联系方式" prop="phone">
<el-input v-model="form.phone" placeholder="请输入" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-radio-group v-model="form.sex">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-row>
<el-row>
<el-form-item label="备注" prop="columnRemarks">
<el-input v-model="form.columnRemarks" style="width: 586px;" rows="3" type="textarea" />
<el-form-item label="用户类型" prop="type">
<el-select v-model="form.type" placeholder="请选择">
<el-option
v-for="(item,index) in userTypeOptions"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-row>
</el-form>
</el-form>
<div class="right-user-img">
<img src="~@/assets/images/user.jpg" alt="">
<el-button type="primary"><i class="iconfont icon-yulan" />摄像头拍摄</el-button>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确定</el-button>
@ -100,20 +94,26 @@
<el-table-column prop="cardNum" label="身份证号" />
<el-table-column prop="libcode" label="馆代码" />
<el-table-column prop="columnOrders" label="已存照片" align="center" />
<el-table-column label="黑名单" align="center" prop="columnStatus">
<el-table-column prop="columnOrders" label="用户类型" align="center" />
<el-table-column label="操作" align="center" prop="columnStatus" width="240">
<template slot-scope="scope">
<el-switch v-model="scope.row.columnStatus" active-color="#409EFF" inactive-color="#F56C6C" :active-value="1" :inactive-value="0" @change="changeStatus(scope.row, scope.row.columnStatus)" />
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="createTime" label="操作时间">
<template slot-scope="scope">
<div>{{ scope.row.createTime | parseTime }}</div>
<el-button size="mini" style="padding: 5px;" @click="addBlackOrVipUser(scope.row,0)">
<i class="iconfont icon-heimingdan" />
加入黑名单
</el-button>
<el-button size="mini" style="padding: 5px;" @click="addBlackOrVipUser(scope.row,1)">
<i class="iconfont icon-yooxi" />
加入VIP
</el-button>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
<pagination v-if="crud.data.length !== 0" />
</div>
<FaceSearch ref="faceSearchRefs" />
<SelfRegister ref="selfRegisterRefs" />
<BatchImport ref="batchImportRefs" />
</div>
</template>
@ -123,16 +123,19 @@ import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
import FaceSearch from './module/faceSearch'
import SelfRegister from './module/selfRegister'
import BatchImport from './module/batchImport'
// import { exportFile } from '@/utils/index'
// import qs from 'qs'
import { mapGetters } from 'vuex'
const defaultForm = { id: null, columnName: null, columnNo: null, columnType: 1, columnOrders: 999, columnContentType: 1, columnStatus: 1, columnRemarks: null }
const defaultForm = { id: null, libcode: null, code: null, name: null, card: null, phone: null, sex: 1, type: 1 }
export default {
name: 'PersonInfoManage',
components: { pagination, crudOperation, rrOperation },
components: { pagination, crudOperation, rrOperation, FaceSearch, SelfRegister, BatchImport },
cruds() {
return CRUD({ title: '人员信息', url: 'api/fonds/initFondsList', crudMethod: { ...crudColumn }, optShow: {
return CRUD({ title: '人员信息', url: 'api/person/initPersonInfo', crudMethod: { ...crudColumn }, optShow: {
add: true,
edit: true,
del: false,
@ -145,34 +148,28 @@ export default {
data() {
return {
permission: {
add: ['admin', 'column:add'],
edit: ['admin', 'column:edit'],
del: ['admin', 'column:del']
add: ['admin', 'face:add'],
edit: ['admin', 'face:edit'],
del: ['admin', 'face:del']
},
enabledTypeOptions: [
{ key: '1', display_name: '全部' },
{ key: '2', display_name: '已存照片' },
{ key: '3', display_name: '暂无照片' }
userTypeOptions: [
{ value: 1, label: '普通用户' },
{ value: 2, label: 'VIP用户' },
{ value: 3, label: '黑名单用户' }
],
fiterBlackUser: false,
rules: {
columnName: [
{ required: true, message: '请输入机构名称', trigger: 'blur' }
],
columnNo: [
{ required: true, message: '请输入栏目编码', trigger: 'blur' }
],
columnType: [
{ required: true, message: '请选择栏目类型', trigger: 'change' }
libcode: [
{ required: true, message: '馆代码不可为空', trigger: 'blur' }
],
columnContentType: [
{ required: true, message: '请选择内容类型', trigger: 'change' }
code: [
{ required: true, message: '读者证号不可为空', trigger: 'blur' }
],
columnOrders: [
{ required: true, message: '请输入排序', trigger: 'blur', type: 'number' }
name: [
{ required: true, message: '姓名不可为空', trigger: 'blur' }
],
columnStatus: [
{ required: true, message: '请选择状态', trigger: 'change' }
type: [
{ required: true, message: '请选择用户类型', trigger: 'change' }
]
}
}
@ -206,6 +203,15 @@ export default {
handleFiterBlackUser(val) {
console.log(val)
},
handleFaceSearch() {
this.$refs.faceSearchRefs.faceSearchVisible = true
},
handleSelfRegister() {
this.$refs.selfRegisterRefs.open()
},
handleBatchImport() {
this.$refs.batchImportRefs.batchImportVisible = true
},
toDelete(datas) {
this.$confirm('此操作将删除当前所选' + this.crud.title + '<span>你是否还要继续?</span>', '提示', {
confirmButtonText: '继续',
@ -229,8 +235,9 @@ export default {
}).catch(() => {
})
},
changeStatus(data, val) {
this.$confirm('此操作将禁用 / 启用栏目 “' + data.fondsName + '”' + '<span>你是否还要继续?</span>', '提示', {
addBlackOrVipUser(data, type) {
const message = type === 1 ? 'VIP' : '黑名单'
this.$confirm('此操作将所选用户加入' + message + '<span>你是否还要继续?</span>', '提示', {
confirmButtonText: '继续',
cancelButtonText: '取消',
type: 'warning',
@ -251,20 +258,25 @@ export default {
</script>
<style lang="scss" scoped>
::v-deep .el-dialog {
.el-dialog__body {
.el-form-item{
&:nth-child(odd){
margin-right: 0 !important;
}
}
}
}
.face-search.el-button:hover,
.face-search.el-button:focus,
.face-search.el-button--info.is-plain:hover,
.face-search.el-button--info.is-plain:focus{
background: #E8F2FF !important;
}
.right-user-img{
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
margin-left: 30px;
border-left: 1px solid #E8E8E8;
img{
display: block;
width: 150px;
height: 200px;
margin: 20px 0;
}
}
</style>

13
src/views/inquiryMachine/column.vue

@ -43,9 +43,9 @@
<el-input v-model="form.title" style="width: 586px;" />
</el-form-item>
</el-row>
<el-form-item label="栏目编码" prop="code">
<!-- <el-form-item label="栏目编码" prop="code">
<el-input v-model="form.code" />
</el-form-item>
</el-form-item> -->
<el-form-item label="栏目类型" prop="type">
<el-select v-model="form.type" placeholder="请选择">
<el-option
@ -85,7 +85,7 @@
<el-table ref="table" v-loading="crud.loading" highlight-current-row style="width: 100%;" height="calc(100vh - 330px)" :data="crud.data" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" align="center" width="55" />
<el-table-column prop="title" label="栏目名称" />
<el-table-column prop="code" label="栏目编码" />
<!-- <el-table-column prop="code" label="栏目编码" /> -->
<el-table-column prop="type" label="栏目类型">
<template slot-scope="scope">
<div v-if="scope.row.type === 1">图文详情</div>
@ -94,7 +94,12 @@
<div v-if="scope.row.type === 4">图文列表-外部跳转</div>
</template>
</el-table-column>
<el-table-column prop="sonCount" label="内容数量" />
<el-table-column prop="sonCount" label="内容数量">
<template slot-scope="scope">
<span v-if="scope.row.sonCount === 0" style="color: rgb(237, 74, 65);">{{ scope.row.sonCount }}</span>
<span v-else>{{ scope.row.sonCount }}</span>
</template>
</el-table-column>
<el-table-column prop="seqencing" label="排序" align="center" />
<el-table-column label="状态" align="center" prop="status">
<template slot-scope="scope">

52
src/views/inquiryMachine/content.vue

@ -12,16 +12,16 @@
<div v-if="node.label.length <= 10" class="tree-text">
<p> {{ node.label }}</p>
<div v-if="data.id !== 0">
<span v-if="data.type !== 1">{{ data.sonCount }} /</span>
<span v-else>{{ data.sonCount }} /1</span>
<span v-if="data.type !== 1"><em :class="data.sonCount === 0 ? 'no-data-style':''">{{ data.sonCount }}</em> /</span>
<span v-else><em :class="data.sonCount === 0 ? 'no-data-style':''">{{ data.sonCount }}</em> /1</span>
</div>
</div>
<el-tooltip v-else effect="dark" :content="node.label" :enterable="false" placement="left">
<span class="tree-text">
<p> {{ node.label }}</p>
<div v-if="data.id !== 0">
<span v-if="data.type !== 1">{{ data.sonCount }} /</span>
<span v-else>{{ data.sonCount }} /1</span>
<span v-if="data.type !== 1"><em :class="data.sonCount === 0 ? 'no-data-style':''">{{ data.sonCount }}</em> /</span>
<span v-else><em :class="data.sonCount === 0 ? 'no-data-style':''">{{ data.sonCount }}</em> /1</span>
</div>
</span>
</el-tooltip>
@ -168,6 +168,7 @@ export default {
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
needRefreshTree: false,
labelName: '内容图片',
currentKeyColumn: {},
rules: {
@ -253,6 +254,17 @@ export default {
item.topicType = this.currentKeyColumn.type
})
this.tableData = this.crud.data
if (this.needRefreshTree) {
this.needRefreshTree = false
this.getQueryTopicTree()
}
},
expandParents(node) {
node.expanded = true
if (node.parent) {
this.expandParents(node.parent)
}
},
//
[CRUD.HOOK.afterToCU](crud, form) {
@ -289,6 +301,12 @@ export default {
this.form.introHtml = this.editorContent
return true
},
[CRUD.HOOK.afterSubmit]() {
this.needRefreshTree = true
},
[CRUD.HOOK.afterDelete]() {
this.needRefreshTree = true
},
imgAdd(pos, $file) {
const _this = this
var formdata = new FormData()
@ -327,6 +345,7 @@ export default {
this.form.introHtml = html
},
getQueryTopicTree() {
this.columnTreeDatas = []
const parent = {}
parent.id = 0
parent.title = '栏目'
@ -334,9 +353,22 @@ export default {
parent.children = res
this.columnTreeDatas.push(parent)
this.$nextTick(() => {
let currentKey
const storedKey = localStorage.getItem('currentColumnKey')
if (storedKey) {
const parsedKey = JSON.parse(storedKey)
if (parsedKey && parsedKey.id) {
currentKey = parsedKey
} else {
currentKey = this.columnTreeDatas[0].children[0]
}
} else {
currentKey = this.columnTreeDatas[0].children[0]
}
Vue.set(this.defaultExpandedKeys, 0, this.columnTreeDatas[0].children[0].id)
this.$refs.tree.setCurrentKey(this.columnTreeDatas[0].children[0].id)
this.handleNodeClick(this.columnTreeDatas[0].children[0])
this.$refs.tree.setCurrentKey(currentKey.id)
this.handleNodeClick(currentKey)
})
})
},
@ -346,6 +378,7 @@ export default {
},
handleNodeClick(val) {
this.currentKeyColumn = val
localStorage.setItem('currentColumnKey', JSON.stringify(val))
console.log(val)
if (val && val.id !== 0) {
this.crud.query.libcode = this.user.fonds.fondsNo
@ -370,6 +403,7 @@ export default {
this.$message({ message: '删除成功', type: 'success', offset: 8 })
this.crud.delAllLoading = false
this.crud.refresh()
this.getQueryTopicTree()
}).catch(err => {
this.crud.delAllLoading = false
console.log(err)
@ -410,6 +444,12 @@ export default {
display: flex;
justify-content: space-between;
align-items: center;
em{
font-style: normal;
&.no-data-style{
color: rgb(237, 74, 65);
}
}
}
}
}

175
src/views/inquiryMachine/form.vue

@ -1,175 +0,0 @@
<template>
<!--表单组件-->
<el-dialog append-to-body :close-on-click-modal="false" :modal-append-to-body="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title">
<div class="setting-dialog">
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="90px">
<el-form-item v-if="selectedMenu.pid === '0'" label="所属父级" prop="parentName">
<el-input v-model="selectedMenu.title" disabled />
</el-form-item>
<el-form-item v-else label="所属父级" prop="parentName">
<el-input v-model="selectedMenu.parentName" disabled />
</el-form-item>
<el-form-item label="编码" prop="code">
<el-input v-model="form.code" disabled />
</el-form-item>
<el-form-item label="名称" prop="title">
<el-input v-model="form.title" />
</el-form-item>
<el-form-item label="菜单类型" prop="type">
<el-select v-model="form.type" style="width: 225px;" @change="changeType">
<el-option v-for="item in selectOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-row v-if="form.type && (form.type === 2 || form.type === 3)">
<!-- @remove-tag="deleteTag" @change="changeColumn" -->
<el-form-item style="width: 100%;" label="栏目绑定" prop="bind" class="is-required selecct-dropdown">
<el-select v-model="form.queryTopicList" style="width: 584px" value-key="id" multiple placeholder="请选择" :popper-append-to-body="false">
<el-option v-for="item in columnDatas" :key="item.id" :label="item.title" :value="item" />
</el-select>
</el-form-item>
</el-row>
<el-form-item label="备注" prop="remarks">
<el-input v-model="form.remarks" type="textarea" :rows="4" style="width: 580px;" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import { FetchQueryTopicTree } from '@/api/inquiryMachine/column'
import { form } from '@crud/crud'
import CRUD from '@crud/crud'
import { mapGetters } from 'vuex'
const defaultForm = { id: null, parentId: null, parentName: null, title: null, code: null, queryTopicList: null, type: null, remarks: null }
export default {
mixins: [
form(function() {
return Object.assign({ libcode: this.user.fonds.fondsNo }, defaultForm)
})
],
props: {
selectedMenu: {
type: Object,
default: function() {
return {}
}
}
},
data() {
return {
pid: null,
isAdd: false,
rules: {
parentId: [
{ required: true, message: '所属父级不可为空', trigger: 'blur' }
],
title: [
{ required: true, message: '名称不可为空', trigger: 'blur' }
],
type: [
{ required: true, message: '菜单类型不可为空', trigger: 'change' }
],
// code: [
// { required: true, message: '', trigger: 'blur' }
// ],
queryTopicList: [
{
required: () => {
return this.form.type === 2 || this.form.type === 3
},
message: '栏目绑定不可为空',
trigger: 'change'
}
]
},
selectOptions: [
{ value: 1, label: '菜单' },
{ value: 2, label: '栏目显示' },
{ value: 3, label: '栏目列表' },
{ value: 4, label: '新书推荐' }
],
columnDatas: [],
bindColumn: []
}
},
computed: {
...mapGetters([
'baseApi',
'user'
])
},
watch: {
selectedMenu: function(newValue, oldValue) {
// console.log('newValue', newValue)
},
'form.type': function(newType) {
if (newType !== 2 && newType !== 3) {
this.form.queryTopicList = null
}
this.$nextTick(() => {
this.$forceUpdate()
})
}
},
created() {
this.getInitQueryTopic()
},
methods: {
getInitQueryTopic() {
FetchQueryTopicTree().then(res => {
this.columnDatas = res
})
},
// changeColumn(value) {
// this.bindColumn = []
// value.forEach(function(data, index) {
// const role = { id: data }
// this.bindColumn.push(role)
// })
// },
// deleteTag(value) {
// this.bindColumn.forEach(function(data, index) {
// if (data.id === value.id) {
// this.bindColumn.splice(index, value)
// }
// })
// },
//
[CRUD.HOOK.afterValidateCU](crud) {
delete crud.form.parentName
delete crud.form.queryMenuTopics
const { queryTopicList, ...otherFields } = crud.form
crud.form = {
queryMenu: otherFields,
queryTopicList: queryTopicList
}
console.log(crud.form)
return true
},
changeType(val) {
console.log('val', val)
// this.isAdd = this.form.type === 2
},
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children
}
return {
id: node.id,
label: node.title,
children: node.children,
isDisabled: node.type !== 2 && node.type !== 3 && node.type !== 5
}
}
}
}
</script>
<style lang="scss" scoped>
</style>

94
src/views/inquiryMachine/menu.vue

@ -71,9 +71,9 @@
<p v-else-if="selectedMenu.type == 3">栏目列表</p>
<p v-else-if="selectedMenu.type == 4">新书推荐</p>
<p v-else />
<div v-if="currentNodeLevel===2" style="flex: 1; display: flex; justify-content: flex-start; align-items: center; margin-left: 10px;">
<i style="cursor: pointer;" class="iconfont" :class="copied ? 'icon-fuzhichenggong1' : 'icon-fuzhi'" @click="copyLink" />
<span style="display: block; flex: 1; text-align: left;">仅1级菜单可生成链接地址</span>
<div v-if="currentNodeLevel===2" style="flex: 1; display: flex; justify-content: flex-start; align-items: center; margin-left: 10px; cursor: pointer;" @click="copyLink">
<!-- <i style="cursor: pointer;" class="iconfont" :class="copied ? 'icon-fuzhichenggong1' : 'icon-fuzhi'" /> -->
<span style="display: block; flex: 1; text-align: left;">点击查看链接仅1级菜单可生成链接地址</span>
</div>
</div>
<div class="info-item">
@ -100,6 +100,16 @@
</div>
</div>
<el-dialog title="地址预览" :close-on-click-modal="false" :modal-append-to-body="false" append-to-body :visible.sync="urlDialogVisible" :before-close="handleClose">
<div class="setting-dialog">
<el-input v-model="finalLink" readonly />
<!-- <div slot="footer" class="dialog-footer">
<el-button @click="handleClose">取消 </el-button>
<el-button type="primary" @click.native="handleLinkConfirm">确定</el-button>
</div> -->
</div>
</el-dialog>
<!--排序对话框组件-->
<sortDialog ref="sort" @treeNodeSort="treeNodeSort" />
@ -141,7 +151,7 @@
<el-input v-model="form.queryMenu.title" />
</el-form-item>
<el-form-item label="菜单类型" prop="queryMenu.type">
<el-select v-model="form.queryMenu.type" style="width: 225px;" @change="changeType">
<el-select v-model="form.queryMenu.type" style="width: 225px;" :disabled="btnType==='edit'" @change="changeType">
<el-option v-for="item in filteredSelectOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
@ -149,7 +159,16 @@
<el-row v-if="form.queryMenu.type && (form.queryMenu.type === 2 || form.queryMenu.type === 3)">
<el-form-item style="width: 100%;" label="栏目绑定" prop="queryTopicList" class="is-required selecct-dropdown">
<el-select ref="selectRef" :key="selectKey" v-model="form.queryTopicList" style="width: 584px" value-key="id" :multiple="form.queryMenu.type !== 2" placeholder="请选择" :popper-append-to-body="false">
<el-option v-for="item in columnDatas" :key="item.id" :label="item.title" :value="item" />
<!-- <el-option v-for="item in columnDatas" :key="item.id" :label="item.title" :value="item" /> -->
<el-option
v-for="item in columnDatas"
:key="item.id"
:label="item.title"
:value="item"
>
<span style="float: left">{{ item.title }}</span>
<span v-if="getTypeText(item.type)" :style="typeTextStyle">{{ getTypeText(item.type) }}</span>
</el-option>
</el-select>
</el-form-item>
</el-row>
@ -263,7 +282,9 @@ export default {
},
selectKey: Math.random(),
btnType: 'add',
currentNodeLevel: 0
currentNodeLevel: 0,
urlDialogVisible: false,
finalLink: null
}
},
computed: {
@ -288,6 +309,13 @@ export default {
}
return this.selectOptions
},
typeTextStyle() {
return {
float: 'right',
color: '#8492a6',
fontSize: '13px'
}
}
},
created() {
@ -295,6 +323,15 @@ export default {
this.initScreenSetting()
},
methods: {
getTypeText(type) {
const typeMap = {
1: '图文详情',
2: '文字列表-图文详情',
3: '图文列表-图文详情',
4: '图文列表-外部跳转'
}
return typeMap[type]
},
getInitQueryTopic(type) {
FetchQueryTopicTree({ 'libcode': this.user.fonds.fondsNo, 'type': type }).then(res => {
this.columnDatas = res
@ -430,31 +467,25 @@ export default {
console.log(`当前节点层级:${node.level}`)
},
changeType(val) {
const currentSelected = this.form.queryTopicList //
let savedData = [] //
const currentSelected = this.form.queryTopicList
let savedData = []
// 1. /
if (currentSelected === null || currentSelected === undefined) {
savedData = [] //
savedData = []
} else if (Array.isArray(currentSelected)) {
savedData = [...currentSelected] //
savedData = [...currentSelected]
} else {
savedData = [currentSelected] // 便
savedData = [currentSelected]
}
// 2. val /
if (val === 2) {
// - null
this.form.queryTopicList = savedData.length > 0 ? savedData[0] : null
} else {
// /-
this.form.queryTopicList = [...savedData]
}
// 3.
this.selectKey = Math.random()
// 4. type=2
this.$nextTick(() => {
this.getInitQueryTopic(val === 2 ? 1 : null)
})
@ -484,6 +515,7 @@ export default {
this.dialogTilte = '编辑菜单'
const editType = this.selectedMenu.type //
const topicList = this.selectedMenu.queryTopicList || [] //
this.changeType(editType)
this.form = {
queryMenu: {
id: this.selectedMenu.id,
@ -643,6 +675,7 @@ export default {
handleClose() {
this.formVisible = false
this.btnLoading = false
this.urlDialogVisible = false
if (this.$refs['form']) {
this.$refs['form'].resetFields()
@ -682,20 +715,21 @@ export default {
}
const linkSeparator = this.menuForm.query_preview.includes('?') ? '&' : '?'
const finalLink = `${this.menuForm.query_preview}${linkSeparator}menuId=${this.selectedMenu.id}`
this.finalLink = `${this.menuForm.query_preview}${linkSeparator}menuId=${this.selectedMenu.id}`
this.urlDialogVisible = true
navigator.clipboard.writeText(finalLink)
.then(() => {
this.copied = true
this.$message.success('链接复制成功!') //
setTimeout(() => {
this.copied = false
}, 2000)
})
.catch(err => {
console.error('无法复制内容: ', err)
this.fallbackCopyText(finalLink) //
})
// navigator.clipboard.writeText(finalLink)
// .then(() => {
// this.copied = true
// this.$message.success('') //
// setTimeout(() => {
// this.copied = false
// }, 2000)
// })
// .catch(err => {
// console.error(': ', err)
// this.fallbackCopyText(finalLink) //
// })
},
fallbackCopyText() {
const textArea = document.createElement('textarea')

Loading…
Cancel
Save