lion 3 months ago
parent a43d22f5f0
commit dc37f4822e

@ -1,6 +1,6 @@
# just a flag
ENV = 'development'
# base api
VUE_APP_BASE_API = https://bd-fangke.ali251.langye.net
VUE_APP_UPLOAD_API = https://bd-fangke.ali251.langye.net/api/admin/upload-file
# base api(与测试/正式环境同一后端域名)
VUE_APP_BASE_API = http://yxbd-fangke.ali251.langye.net
VUE_APP_UPLOAD_API = http://yxbd-fangke.ali251.langye.net/api/admin/upload-file

@ -2,5 +2,5 @@
ENV = 'production'
# base api
VUE_APP_BASE_API = https://bd-fangke.ali251.langye.net
VUE_APP_UPLOAD_API = https://bd-fangke.ali251.langye.net/api/admin/upload-file
VUE_APP_BASE_API =
VUE_APP_UPLOAD_API = /api/admin/upload-file

@ -4,5 +4,5 @@ NODE_ENV = production
ENV = 'staging'
# base api
VUE_APP_BASE_API = '/stage-api'
VUE_APP_BASE_API = https://yxbd-fangke.ali251.langye.net
VUE_APP_UPLOAD_API = https://yxbd-fangke.ali251.langye.net/api/admin/upload-file

@ -32,7 +32,8 @@
"vue": "2.6.10",
"vue-count-to": "^1.0.13",
"vue-router": "3.0.6",
"vuex": "3.1.0"
"vuex": "3.1.0",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@vue/cli-plugin-babel": "4.4.4",

@ -11,10 +11,10 @@ export function save(data) {
export function listuser(params) {
return request({
url: '/api/admin/admin',
method: 'get',
method: 'get',
params
})
}
}
export function del(data) {
return request({
@ -22,13 +22,33 @@ export function del(data) {
method: 'post',
data
})
}
}
export function setRoles(data) {
return request({
url: '/api/admin/admin/set-roles',
method: 'post',
data
})
}
}
export function importPreview(data) {
return request({
url: '/api/admin/admin/import-preview',
method: 'post',
data,
timeout: 120000,
isLoading: true
})
}
export function importSubmit(data) {
return request({
url: '/api/admin/admin/import-submit',
method: 'post',
data,
timeout: 120000,
isLoading: true
})
}

@ -0,0 +1,42 @@
import request from "@/utils/request";
export function getList(params) {
return request({
url: "/api/admin/vip-customer/index",
method: "get",
params
});
}
export function show(params) {
return request({
method: "get",
url: "/api/admin/vip-customer/show",
params
});
}
export function save(data) {
return request({
method: "post",
url: "/api/admin/vip-customer/save",
data
});
}
export function destroy(params) {
return request({
method: "get",
url: "/api/admin/vip-customer/destroy",
params
});
}
export function importVip(data) {
return request({
method: "post",
url: "/api/admin/vip-customer/import",
data
});
}

@ -60,8 +60,8 @@
this.setOptions(this.chartData);
},
setOptions(chartdata) {
console.log(chartdata.xArr)
this.chart.setOption({
color: ['#409EFF', '#8B6EF5', '#2BB673', '#FF8A00'],
dataZoom: [
//x
// {
@ -127,30 +127,30 @@
xAxis: [{
type: 'category',
data: chartdata.xArr,
axisLine: {
show: false //线
},
axisTick: {
show: false //
axisLine: {
show: false //线
},
axisTick: {
show: false //
},
}],
yAxis: [{
type: 'value',
minInterval: 1,
axisLine: {
show: false //线
},
axisTick: {
show: false //
minInterval: 1,
axisLine: {
show: false //线
},
axisTick: {
show: false //
},
}],
}],
radiusArr:[],
series: [{
name: '数据',
type: 'pie',
stack: 'vistors',
barWidth: '60%',
radius: chartdata.radiusArr,
barWidth: '60%',
radius: chartdata.radiusArr,
center: ['50%', '45%'],
data: chartdata.yArr,
animationDuration
@ -160,4 +160,4 @@
}
}
}
</script>
</script>

@ -10,15 +10,15 @@
<div class="boxcontentsubtitle">总人数/总入厂人数</div>
<div class="boxcontent">
<div class="boxcontentitem">
<div class="boxcontentitem-big">
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.common_visit.total"
:duration="3600" />
</span>
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.common_visit.enter_visit"
:duration="3600" />
</span>
<div class="boxcontentitem-big">
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.common_visit.total"
:duration="3600" />
</span>
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.common_visit.enter_visit"
:duration="3600" />
</span>
</div>
<div class="per">核销比{{toCaculateper(totaldata.common_visit.enter_visit,totaldata.common_visit.total)}}</div>
@ -29,13 +29,13 @@
<div class="boxcontent">
<div class="boxcontentitem">
<div class="boxcontentitem-big">
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.common_visit.today_total"
:duration="3600" />
</span>
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.common_visit.today_enter_visit"
:duration="3600" />
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.common_visit.today_total"
:duration="3600" />
</span>
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.common_visit.today_enter_visit"
:duration="3600" />
</span>
</div>
<div class="per">核销比{{toCaculateper(totaldata.common_visit.today_enter_visit,totaldata.common_visit.today_total)}}</div>
@ -50,38 +50,78 @@
<i class="iconfont icon-banqianguanli statIcon"></i>
</div>
<div class="bline"></div>
<div class="boxcontentsubtitle">总人数/总核销人数</div>
<div class="boxcontent">
<div class="boxcontentitem">
<div class="boxcontentitem-big">
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.work_visit.total"
:duration="3600" />
</span>
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.work_visit.enter_visit"
:duration="3600" />
</span>
</div>
<div class="per">核销比{{toCaculateper(totaldata.work_visit.enter_visit,totaldata.work_visit.total)}}</div>
</div>
</div>
<div class="blinefull"></div>
<div class="boxcontentsubtitle">今日人数/今日核销人数</div>
<div class="boxcontent">
<div class="boxcontentitem">
<div class="boxcontentitem-big">
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.work_visit.today_total"
:duration="3600" />
</span>
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.work_visit.today_enter_visit"
:duration="3600" />
</span>
</div>
<div class="per">核销比{{toCaculateper(totaldata.work_visit.today_enter_visit,totaldata.work_visit.today_total)}}</div>
</div>
<div class="boxcontentsubtitle">总人数/总核销人数</div>
<div class="boxcontent">
<div class="boxcontentitem">
<div class="boxcontentitem-big">
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.work_visit.total"
:duration="3600" />
</span>
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.work_visit.enter_visit"
:duration="3600" />
</span>
</div>
<div class="per">核销比{{toCaculateper(totaldata.work_visit.enter_visit,totaldata.work_visit.total)}}</div>
</div>
</div>
<div class="blinefull"></div>
<div class="boxcontentsubtitle">今日人数/今日核销人数</div>
<div class="boxcontent">
<div class="boxcontentitem">
<div class="boxcontentitem-big">
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.work_visit.today_total"
:duration="3600" />
</span>
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.work_visit.today_enter_visit"
:duration="3600" />
</span>
</div>
<div class="per">核销比{{toCaculateper(totaldata.work_visit.today_enter_visit,totaldata.work_visit.today_total)}}</div>
</div>
</div>
</div>
<div class="box box3">
<div class="boxtitle">
<span>VIP客户</span>
<i class="el-icon-s-custom statIcon"></i>
</div>
<div class="bline"></div>
<div class="boxcontentsubtitle">总人数/总核销人数</div>
<div class="boxcontent">
<div class="boxcontentitem">
<div class="boxcontentitem-big">
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.vip_visit.total"
:duration="3600" />
</span>
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.vip_visit.enter_visit"
:duration="3600" />
</span>
</div>
<div class="per">核销比{{toCaculateper(totaldata.vip_visit.enter_visit,totaldata.vip_visit.total)}}</div>
</div>
</div>
<div class="blinefull"></div>
<div class="boxcontentsubtitle">今日人数/今日核销人数</div>
<div class="boxcontent">
<div class="boxcontentitem">
<div class="boxcontentitem-big">
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.vip_visit.today_total"
:duration="3600" />
</span>
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.vip_visit.today_enter_visit"
:duration="3600" />
</span>
</div>
<div class="per">核销比{{toCaculateper(totaldata.vip_visit.today_enter_visit,totaldata.vip_visit.today_total)}}</div>
</div>
</div>
</div>
<div class="box box4">
@ -91,42 +131,42 @@
<i class="iconfont icon-cheliangdangan statIcon"></i>
</div>
<div class="bline"></div>
<div class="boxcontentsubtitle">总预约数/总核销数</div>
<div class="boxcontent">
<div class="boxcontentitem">
<div class="boxcontentitem-big">
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.car_visit.total"
:duration="3600" />
</span>
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.car_visit.enter_visit"
:duration="3600" />
</span>
</div>
<div class="per">核销比{{toCaculateper(totaldata.car_visit.enter_visit,totaldata.car_visit.total)}}</div>
</div>
</div>
<div class="blinefull"></div>
<div class="boxcontentsubtitle">今日预约数/今日核销数</div>
<div class="boxcontent">
<div class="boxcontentitem">
<div class="boxcontentitem-big">
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.car_visit.today_total"
:duration="3600" />
</span>
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.car_visit.today_enter_visit"
:duration="3600" />
</span>
</div>
<div class="per">核销比{{toCaculateper(totaldata.work_visit.today_enter_visit,totaldata.car_visit.today_total)}}</div>
</div>
<div class="boxcontentsubtitle">总预约数/总核销数</div>
<div class="boxcontent">
<div class="boxcontentitem">
<div class="boxcontentitem-big">
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.car_visit.total"
:duration="3600" />
</span>
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.car_visit.enter_visit"
:duration="3600" />
</span>
</div>
<div class="per">核销比{{toCaculateper(totaldata.car_visit.enter_visit,totaldata.car_visit.total)}}</div>
</div>
</div>
<div class="blinefull"></div>
<div class="boxcontentsubtitle">今日预约数/今日核销数</div>
<div class="boxcontent">
<div class="boxcontentitem">
<div class="boxcontentitem-big">
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.car_visit.today_total"
:duration="3600" />
</span>
<span>
<count-to separator="" :start-val="0" :end-val="totaldata.car_visit.today_enter_visit"
:duration="3600" />
</span>
</div>
<div class="per">核销比{{toCaculateper(totaldata.car_visit.today_enter_visit,totaldata.car_visit.today_total)}}</div>
</div>
</div>
</div>
</div>
</div>
</template>
@ -146,20 +186,26 @@
"common_visit": {
"enter_visit": 0,
"today_enter_visit": 0,
"today_total": 0,
"today_total": 0,
"total":0
},
"work_visit": {
"enter_visit": 0,
"today_enter_visit": 0,
"today_total": 0,
"today_total": 0,
"total":0
},
"car_visit": {
"enter_visit": 0,
"today_enter_visit": 0,
"today_total": 0,
"today_total": 0,
"total":0
},
"vip_visit": {
"enter_visit": 0,
"today_enter_visit": 0,
"today_total": 0,
"total": 0
}
}
}
@ -211,7 +257,7 @@
.box {
position: relative;
width: 33%;
width: 24%;
margin-left: 0.5%;
margin-right: 0.5%;
// margin-bottom: 2.375rem;
@ -476,4 +522,4 @@
}
}
}
</style>
</style>

@ -37,41 +37,53 @@
chartData: {},
pieData:{
// xArr:['访','访',''],
yArr:[
{
value:20,
value:0,
name:"普通访客"
},{
value:40,
value:0,
name:"施工访客"
},{
value:40,
value:0,
name:"物流车辆"
},{
value:0,
name:"VIP客户"
}],
radiusArr:'50%'
},
LineData:{
xArr: ['9:00-10:00','10:00-11:00','11:00-12:00','12:00-13:00'],
legendArr: ["普通访客", "施工访客", "物流车辆"],
legendArr: ["普通访客", "施工访客", "物流车辆", "VIP客户"],
series:[
{
name: '普通访客',
type: 'line',
stack: 'Total',
data: [20,10,40,70]
data: [0,0,0,0],
itemStyle: { color: '#409EFF' }
},
{
name: '施工访客',
type: 'line',
stack: 'Total',
data: [15,75,35,45]
data: [0,0,0,0],
itemStyle: { color: '#8B6EF5' }
},
{
name: '物流车辆',
type: 'line',
stack: 'Total',
data: [75,35,60,10]
data: [0,0,0,0],
itemStyle: { color: '#2BB673' }
},
{
name: 'VIP客户',
type: 'line',
stack: 'Total',
data: [0,0,0,0],
itemStyle: { color: '#FF8A00' }
}
]
}
@ -87,26 +99,53 @@
methods: {
async loadData() {
await getChartsHome().then((res) => {
console.log(res);
this.list = res.list;
// this.chartData = res;
// let _business_data = [];
// let _collect_data = [];
// res.business_data.map(item => {
// _business_data.push(item.server_money_total)
// _collect_data.push(item.collect_money)
// })
// this.business_data = _business_data;
// this.collect_data = _collect_data;
// let _customerArr = [];
// let _orderArr = [];
// res.order_data.map(item => {
// _customerArr.push(item.active_customer)
// _orderArr.push(item.order_total)
// })
// this.customerArr = _customerArr;
// this.orderArr = _orderArr;
this.pieData = {
...this.pieData,
yArr: [
{ value: res.list?.common_visit?.total || 0, name: "普通访客" },
{ value: res.list?.work_visit?.total || 0, name: "施工访客" },
{ value: res.list?.car_visit?.total || 0, name: "物流车辆" },
{ value: res.list?.vip_visit?.total || 0, name: "VIP客户" }
]
};
const dateList = res.all_date_list || [];
this.LineData = {
...this.LineData,
xArr: dateList.map(item => item.date?.slice(5) || item.date),
series: [
{
name: '普通访客',
type: 'line',
stack: 'Total',
data: dateList.map(item => item.common_visit || 0),
itemStyle: { color: '#409EFF' }
},
{
name: '施工访客',
type: 'line',
stack: 'Total',
data: dateList.map(item => item.work_visit || 0),
itemStyle: { color: '#8B6EF5' }
},
{
name: '物流车辆',
type: 'line',
stack: 'Total',
data: dateList.map(item => item.car_visit || 0),
itemStyle: { color: '#2BB673' }
},
{
name: 'VIP客户',
type: 'line',
stack: 'Total',
data: dateList.map(item => item.vip_visit || 0),
itemStyle: { color: '#FF8A00' }
}
]
};
}).catch()
},
init() {

@ -10,6 +10,7 @@
<el-input style="width: 200px;margin-right: 10px" v-model="searchFields.keyword" placeholder="关键字搜索" />
<Button type="primary" @click="load" style="margin-left: 10px">查询</Button>
<Button type="primary" @click="edit()" style="margin-left: 10px">新增用户</Button>
<Button type="primary" @click="openImportDialog" style="margin-left: 10px">导入</Button>
</div>
</slot>
</LxHeader>
@ -99,6 +100,60 @@
<el-button type="primary" @click="submitForm('form')"> </el-button>
</div>
</el-dialog>
<el-dialog title="导入用户" :visible.sync="importDialogVisible" width="980px">
<div style="margin-bottom:8px;color:#606266;line-height:1.6;">
模板列<b>姓名用户名手机号所属部门职位生日邮箱</b>必填姓名用户名手机号所属部门可选列 <b>密码</b>
密码规则<b>新建</b>用户密码留空则初始为Admin+当前年份填写则使用填写密码<b>已存在</b>用户密码留空则不修改密码填写则重置为填写密码
</div>
<div style="margin-bottom: 10px;display:flex;gap:10px;align-items:center;">
<el-button size="mini" type="primary" plain @click="downloadTemplate"></el-button>
<el-upload
action="#"
:http-request="handleImportHttpRequest"
:show-file-list="false"
accept=".xls,.xlsx,.csv"
>
<el-button size="mini" type="primary">选择文件并预览</el-button>
</el-upload>
<span style="color:#999;">支持 `.xls/.xlsx/.csv`请先预览再导入</span>
</div>
<div v-if="previewSummary.missingHeaders.length" style="margin-bottom:10px;color:#f56c6c;">
模板字段缺失{{ previewSummary.missingHeaders.join("、") }}
</div>
<div v-else style="margin-bottom:10px;color:#606266;">
{{ previewRows.length }} 已存在用户 {{ previewSummary.existsCount }} 缺失部门 {{ previewSummary.missingDepartmentCount }}
</div>
<el-table v-if="previewRows.length" :data="previewRows" border max-height="420">
<el-table-column prop="line_no" label="行号" width="70"></el-table-column>
<el-table-column prop="name" label="姓名" min-width="110"></el-table-column>
<el-table-column prop="username" label="用户名" min-width="140"></el-table-column>
<el-table-column prop="mobile" label="手机号" min-width="130"></el-table-column>
<el-table-column prop="department" label="所属部门" min-width="140"></el-table-column>
<el-table-column prop="position" label="职位" min-width="100"></el-table-column>
<el-table-column prop="birthday" label="生日" min-width="110"></el-table-column>
<el-table-column prop="email" label="邮箱" min-width="160"></el-table-column>
<el-table-column label="密码" min-width="200">
<template slot-scope="scope">
<span v-if="scope.row.password_filled"></span>
<span v-else style="color:#909399;">未填新建默认 Admin+年份已存在不改密</span>
</template>
</el-table-column>
<el-table-column label="预览结果" min-width="260">
<template slot-scope="scope">
<span v-if="scope.row.message" :style="{color: scope.row.department_exists ? '#e6a23c' : '#f56c6c'}">
{{ scope.row.message }}
</span>
<span v-else style="color:#67c23a;">可导入</span>
</template>
</el-table-column>
</el-table>
<div v-else style="color:#999;">请先选择导入文件进行预览</div>
<span slot="footer" class="dialog-footer">
<el-button @click="importDialogVisible = false">取消</el-button>
<el-button type="primary" :disabled="!importFile || previewSummary.missingHeaders.length > 0 || previewSummary.missingDepartmentCount > 0" @click="submitImport"></el-button>
</span>
</el-dialog>
</div>
</div>
</template>
@ -109,9 +164,12 @@
save,
setRoles,
listuser,
del
del,
importPreview,
importSubmit
} from "../../api/system/user.js";
import {listCommondepartment} from '../../api/common.js'
import * as XLSX from "xlsx";
import {
list
} from "../../api/system/role.js";
@ -182,7 +240,15 @@
searchFields: {
keyword: ""
},
tableData: []
tableData: [],
importDialogVisible: false,
importFile: null,
previewRows: [],
previewSummary: {
existsCount: 0,
missingDepartmentCount: 0,
missingHeaders: []
}
}
},
methods: {
@ -310,6 +376,106 @@
this.$refs[formName].resetFields();
that.dialogFormVisible = false;
},
openImportDialog() {
this.importDialogVisible = true;
this.importFile = null;
this.previewRows = [];
this.previewSummary = {
existsCount: 0,
missingDepartmentCount: 0,
missingHeaders: []
};
},
downloadTemplate() {
// 使 aoa_to_sheet 1 json_to_sheet /
const header = ["姓名", "用户名", "手机号", "所属部门", "职位", "生日", "邮箱", "密码"];
const example = ["张三", "zhangsan", "13800000000", "生产部", "总监", "1990-01-01", "zhangsan@example.com", ""];
const ws = XLSX.utils.aoa_to_sheet([header, example]);
ws["!cols"] = [
{ wch: 10 },
{ wch: 12 },
{ wch: 14 },
{ wch: 14 },
{ wch: 10 },
{ wch: 12 },
{ wch: 28 },
{ wch: 10 }
];
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "用户导入");
XLSX.writeFile(wb, "用户导入模板.xlsx");
},
// 使 http-request before-upload + async
handleImportHttpRequest(option) {
const file = option.file;
this.runImportPreview(file);
},
async runImportPreview(file) {
const isAllow = /\.(xls|xlsx|csv)$/i.test(file.name);
if (!isAllow) {
this.$Message.warning("仅支持xls/xlsx/csv格式");
return;
}
this.importFile = file;
const formData = new FormData();
formData.append("file", file);
try {
const res = await importPreview(formData);
this.previewRows = res.rows || [];
this.previewSummary.existsCount = res.exists_count || 0;
this.previewSummary.missingDepartmentCount = (res.missing_department_rows || []).length;
this.previewSummary.missingHeaders = [];
if (this.previewSummary.existsCount > 0) {
this.$Message.warning(`检测到${this.previewSummary.existsCount}个已存在用户名,导入时可选择是否更新`);
}
if (this.previewSummary.missingDepartmentCount > 0) {
this.$Message.error("存在未创建的部门,请先创建部门后再导入");
}
} catch (error) {
this.previewRows = [];
const msg =
(error && error.response && error.response.data && (error.response.data.errmsg || error.response.data.message)) ||
(error && error.message) ||
"";
const headerMatch = msg.match(/(.+字段不存在)/g);
this.previewSummary.missingHeaders = headerMatch || [];
this.$Message.warning(msg || "预览失败");
}
},
submitImport() {
if (!this.importFile) {
this.$Message.warning("请先选择导入文件");
return;
}
if (this.previewSummary.missingDepartmentCount > 0) {
this.$Message.error("请先创建部门,再执行导入");
return;
}
const doImport = async (forceUpdate) => {
const formData = new FormData();
formData.append("file", this.importFile);
formData.append("force_update", forceUpdate ? 1 : 0);
const result = await importSubmit(formData);
this.$Message.success(`导入完成:新增${result.created || 0},更新${result.updated || 0}`);
this.importDialogVisible = false;
this.load();
};
if (this.previewSummary.existsCount > 0) {
this.$Modal.confirm({
title: `用户已存在,是否直接更新?`,
content: "检测到部分用户名已存在,确认后将直接更新这些用户信息。",
onOk: () => doImport(true).catch((error) => {
const message = error?.response?.data?.message || "导入失败";
this.$Message.error(message);
}),
});
return;
}
doImport(false).catch((error) => {
const message = error?.response?.data?.message || "导入失败";
this.$Message.error(message);
});
},
setrole(obj) {
var that = this;
var rolelist = obj.rolelist;

@ -13,33 +13,38 @@
<el-option v-for="item in statusList" :key="item.id" :label="item.value" :value="item.id">
</el-option>
</el-select>
<div style="margin-right: 10px;">访客类型</div>
<el-select v-model="select.type" clearable placeholder="全部" style="width: 130px;margin-right: 10px;">
<el-option v-for="item in typeList" :key="item.id" :label="item.value" :value="item.id">
</el-option>
</el-select>
<div style="margin:0 10px;">只看自己被访记录</div>
<el-switch
v-model="select.my_accept_admin"
active-color="#004593"
inactive-color="#ddd"
:active-value="1"
:inactive-value="0"
>
</el-switch>
<div v-if="is_admin" style="margin:0 10px;"></div>
<el-switch
v-if="is_admin"
v-model="select.my_audit"
active-color="#004593"
inactive-color="#ddd"
:active-value="1"
:inactive-value="0"
>
</el-switch>
<div style="margin:0 10px;">只看自己创建</div>
<el-switch
v-model="select.my_self"
active-color="#004593"
inactive-color="#ddd"
:active-value="1"
:inactive-value="0"
>
<el-switch
v-model="select.my_accept_admin"
active-color="#004593"
inactive-color="#ddd"
:active-value="1"
:inactive-value="0"
>
</el-switch>
<div v-if="is_admin" style="margin:0 10px;"></div>
<el-switch
v-if="is_admin"
v-model="select.my_audit"
active-color="#004593"
inactive-color="#ddd"
:active-value="1"
:inactive-value="0"
>
</el-switch>
<div style="margin:0 10px;">只看自己创建</div>
<el-switch
v-model="select.my_self"
active-color="#004593"
inactive-color="#ddd"
:active-value="1"
:inactive-value="0"
>
</el-switch>
<el-button @click="getList" slot="reference" size="medium" type="primary" style="margin-left: 10px">查询
</el-button>
@ -51,36 +56,36 @@
<xy-table :table-item="table" :list="data" :total="total"
@pageSizeChange="e => {select.page_size = e;select.page = 1;getList()}"
@pageIndexChange="e => {select.page = e;getList()}">
<template v-slot:btns>
<el-table-column fixed="right" label="操作" width="180" header-align="center">
<template slot-scope="scope">
<i-button
style="margin-right:6px"
type="primary"
size="small"
@click="checkRecords(scope.row)"
>
审核
</i-button>
<Poptip
confirm
:transfer="true"
title="确认要删除吗"
@on-ok="deleteStudy(scope.row)"
>
<i-button
style="margin-right:6px"
type="error"
size="small"
>
删除
</i-button>
</Poptip>
</template>
</el-table-column>
@pageIndexChange="e => {select.page = e;getList()}">
<template v-slot:btns>
<el-table-column fixed="right" label="操作" width="180" header-align="center">
<template slot-scope="scope">
<i-button
style="margin-right:6px"
type="primary"
size="small"
@click="checkRecords(scope.row)"
>
审核
</i-button>
<Poptip
confirm
:transfer="true"
title="确认要删除吗"
@on-ok="deleteStudy(scope.row)"
>
<i-button
style="margin-right:6px"
type="error"
size="small"
>
删除
</i-button>
</Poptip>
</template>
</el-table-column>
</template>
</xy-table>
</xy-table>
<showVisit ref="showVisit" @refresh="getList"></showVisit>
</div>
</template>
@ -89,11 +94,11 @@
import {
getList,
destroy
} from '@/api/visit/record.js'
import showVisit from '@/views/visit/component/showVisit'
} from '@/api/visit/record.js'
import showVisit from '@/views/visit/component/showVisit'
import store from "@/store/modules/user.js"
export default {
components: {
components: {
showVisit
},
data() {
@ -104,13 +109,20 @@
page_size: 10,
keyword: "",
audit_status: 0,
type: '',
my_accept_admin: 0,
my_audit: 1,
my_self:0,
my_audit: 1,
my_self:0,
is_auth:0
},
},
is_admin:false,
selectRange: [],
typeList: [
{ id: 1, value: '普通访客' },
{ id: 2, value: '施工访客' },
{ id: 3, value: '物流车辆' },
{ id: 4, value: 'VIP访客' }
],
statusList: [{
id: -1,
value: '待学习'
@ -165,19 +177,19 @@
sortable: false,
prop: 'audit_status_text',
width: 120
},{
label: '被访人',
sortable: false,
prop: 'accept_admin.name',
width: 120,
},{
label: '被访人',
sortable: false,
prop: 'accept_admin.name',
width: 120,
},
{
label: '是否随访',
sortable: false,
prop: 'follw_people',
width: 80,
formatter:(cell, data, value)=>{
return value.length>0?'是':'否'
width: 80,
formatter:(cell, data, value)=>{
return value.length>0?'是':'否'
}
},
{
@ -190,9 +202,9 @@
label: '证件类型',
sortable: false,
prop: 'credent',
width: 120,
formatter:(cell, data, value)=>{
return value==1?'身份证':'护照'
width: 120,
formatter:(cell, data, value)=>{
return value==1?'身份证':'护照'
},
},
{
@ -236,23 +248,23 @@
{
label: '创建人',
sortable: false,
prop: 'admin.name',
width: 120,
formatter(cell, data, value){
return value?value:''
prop: 'admin.name',
width: 120,
formatter(cell, data, value){
return value?value:''
}
}
]
}
},
computed: {},
mounted() {
const state = store.state
console.log("state",state)
if(state.myRoles && state.myRoles.includes('系统管理员')){
this.select.my_audit = 0
this.is_admin = true
}
mounted() {
const state = store.state
console.log("state",state)
if(state.myRoles && state.myRoles.includes('系统管理员')){
this.select.my_audit = 0
this.is_admin = true
}
console.log(this.is_admin,state.myRoles.includes('系统管理员'))
this.getList()
},
@ -263,7 +275,7 @@
this.data = res.data
this.total = res.total
},
deleteStudy(row) {
deleteStudy(row) {
console.log(row)
destroy({
id: row.id
@ -271,13 +283,13 @@
this.$successMessage('destroy', '拜访记录')
this.getList()
})
},
checkRecords(row){
this.$refs['showVisit'].id = row.id
this.$refs['showVisit'].formDataType='checkrecord'
this.$refs['showVisit'].isShow = true
// this.$refs['checkRecord'].id = row.id
// this.$refs['checkRecord'].isShow = true
},
checkRecords(row){
this.$refs['showVisit'].id = row.id
this.$refs['showVisit'].formDataType='checkrecord'
this.$refs['showVisit'].isShow = true
// this.$refs['checkRecord'].id = row.id
// this.$refs['checkRecord'].isShow = true
}
},
}
@ -317,4 +329,4 @@
line-height: 21px;
}
}
</style>
</style>

@ -73,9 +73,9 @@
<div class="xy-table-item-label">
车辆类型
</div>
<div class="xy-table-item-content">
<el-radio-group v-model="form.plate" style="width:300px">
<el-radio v-for="item in parkAreaList" :key="item.value" :label="item.value">{{item.value}}</el-radio>
<div class="xy-table-item-content">
<el-radio-group v-model="form.plate" style="width:300px">
<el-radio v-for="item in parkAreaList" :key="item.value" :label="item.value">{{item.value}}</el-radio>
</el-radio-group>
<!-- <el-input v-model="form.plate" placeholder="请输入车牌号"></el-input> -->
</div>
@ -99,6 +99,21 @@
</div>
</div>
</template>
<template v-slot:vip_source v-if="visitType==4">
<div class="xy-table-item">
<div class="xy-table-item-label">
VIP来源
</div>
<div class="xy-table-item-content" style="display:flex;align-items:center;gap:10px;">
<el-select filterable clearable v-model="vipSelectId" :loading="vipLoading" placeholder="请选择VIP客户姓名/手机号)"
style="width:320px" @change="handleVipSelect">
<el-option v-for="item in vipOptions" :key="item.id" :label="`${item.name || ''} / ${item.mobile || ''}`" :value="item.id">
</el-option>
</el-select>
<Button type="primary" ghost @click="openVipCreate">VIP</Button>
</div>
</div>
</template>
<template v-slot:name>
<div class="xy-table-item">
<div class="xy-table-item-label">
@ -150,78 +165,78 @@
<el-input v-model="form.company_name" placeholder="请输入单位名称"></el-input>
</div>
</div>
</template>
<template v-slot:cda v-if="visitType==1&&visitAreaText=='生产区'">
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;">*</span>CDA编号
</div>
<div class="xy-table-item-content">
<el-input v-model="form.cda" placeholder="请输入CDA编号(如无注明原因)"></el-input>
</div>
</div>
</template>
<!-- <template v-slot:cars v-if="visitType==3">
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;"></span>到访车辆
</div>
<div class="xy-table-item-content">
<div v-for="(item,index) in form.cars">
<el-input v-model="form.cars[index]" placeholder="请输入到访车辆"></el-input>
</div>
</div>
</div>
</template> -->
<template v-slot:cars v-if="visitType==3">
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;">*</span>到访车辆
</div>
<div class="xy-table-item-content">
<Button type="primary" icon="md-add" style="margin-bottom: 10px"
@click="carsList.push({car:''})">新增到访车辆</Button>
<xy-table style="width: 620px" :height="180" :is-page="false" :list="carsList"
:table-item="carTable">
<template v-slot:btns>
<el-table-column label="操作" width="90" header-align="center" align="center">
<template v-slot:default="scope">
<Button size="small" type="primary" ghost
@click="carsList.splice(scope.$index, 1)">删除</Button>
</template>
</el-table-column>
</template>
</xy-table>
</div>
</div>
</template>
<template v-slot:carsno v-else>
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;"></span>到访车辆
</div>
<div class="xy-table-item-content">
<Button type="primary" icon="md-add" style="margin-bottom: 10px"
@click="carsList.push({car:''})">新增到访车辆</Button>
<xy-table style="width: 620px" :height="180" :is-page="false" :list="carsList"
:table-item="carTable">
<template v-slot:btns>
<el-table-column label="操作" width="90" header-align="center" align="center">
<template v-slot:default="scope">
<Button size="small" type="primary" ghost
@click="carsList.splice(scope.$index, 1)">删除</Button>
</template>
</el-table-column>
</template>
</xy-table>
</div>
</div>
</template>
<template v-slot:cda v-if="visitType==1&&visitAreaText=='生产区'">
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;">*</span>CDA编号
</div>
<div class="xy-table-item-content">
<el-input v-model="form.cda" placeholder="请输入CDA编号(如无注明原因)"></el-input>
</div>
</div>
</template>
<!-- <template v-slot:cars v-if="visitType==3">
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;"></span>到访车辆
</div>
<div class="xy-table-item-content">
<div v-for="(item,index) in form.cars">
<el-input v-model="form.cars[index]" placeholder="请输入到访车辆"></el-input>
</div>
</div>
</div>
</template> -->
<template v-slot:cars v-if="visitType==3">
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;">*</span>到访车辆
</div>
<div class="xy-table-item-content">
<Button type="primary" icon="md-add" style="margin-bottom: 10px"
@click="carsList.push({car:''})">新增到访车辆</Button>
<xy-table style="width: 620px" :height="180" :is-page="false" :list="carsList"
:table-item="carTable">
<template v-slot:btns>
<el-table-column label="操作" width="90" header-align="center" align="center">
<template v-slot:default="scope">
<Button size="small" type="primary" ghost
@click="carsList.splice(scope.$index, 1)">删除</Button>
</template>
</el-table-column>
</template>
</xy-table>
</div>
</div>
</template>
<template v-slot:carsno v-else>
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;"></span>到访车辆
</div>
<div class="xy-table-item-content">
<Button type="primary" icon="md-add" style="margin-bottom: 10px"
@click="carsList.push({car:''})">新增到访车辆</Button>
<xy-table style="width: 620px" :height="180" :is-page="false" :list="carsList"
:table-item="carTable">
<template v-slot:btns>
<el-table-column label="操作" width="90" header-align="center" align="center">
<template v-slot:default="scope">
<Button size="small" type="primary" ghost
@click="carsList.splice(scope.$index, 1)">删除</Button>
</template>
</el-table-column>
</template>
</xy-table>
</div>
</div>
</template>
<template v-slot:follw_people v-if="visitType==1||visitType==2">
<template v-slot:follw_people v-if="visitType==1||visitType==2||visitType==4">
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;"></span>随访人员
@ -261,9 +276,9 @@
</div>
</div>
</div>
</template> -->
</template> -->
<template v-slot:visitorinfos v-if="visitType==1||visitType==2">
<template v-slot:visitorinfos v-if="visitType==1||visitType==2||visitType==4">
<div class="xy-table-item">
<div class="xy-table-item-content" style="width:400px">
被访人信息
@ -271,7 +286,7 @@
</div>
</template>
<template v-slot:accept_admin_id v-if="visitType==1||visitType==2">
<template v-slot:accept_admin_id v-if="visitType==1||visitType==2||visitType==4">
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;">*</span>人员
@ -283,26 +298,26 @@
</el-select>
</div>
</div>
</template>
<template v-slot:visitorinfos1 v-if="visitAreaText=='生产区'">
<div class="xy-table-item">
<div class="xy-table-item-content" style="width:400px">
陪同人信息
</div>
</div>
</template>
<template v-slot:accompany_id v-if="visitAreaText=='生产区'">
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;">*</span>人员
</div>
<div class="xy-table-item-content">
<el-select filterable v-model="form.accompany_id" @change='changeAdmin' placeholder="请选择" style="width:100%">
<el-option v-for="item in adminList" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</div>
</div>
</template>
<template v-slot:visitorinfos1 v-if="visitAreaText=='生产区'">
<div class="xy-table-item">
<div class="xy-table-item-content" style="width:400px">
陪同人信息
</div>
</div>
</template>
<template v-slot:accompany_id v-if="visitAreaText=='生产区'">
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;">*</span>人员
</div>
<div class="xy-table-item-content">
<el-select filterable v-model="form.accompany_id" @change='changeAdmin' placeholder="请选择" style="width:100%">
<el-option v-for="item in adminList" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</div>
</div>
</template>
<template v-slot:accept_goods_admin_id v-if="visitType==3">
<div class="xy-table-item">
@ -321,6 +336,34 @@
</xy-dialog>
<el-dialog title="新增VIP用户" :visible.sync="vipCreateVisible" width="560px">
<el-form :model="vipCreateForm" :rules="vipCreateRules" ref="vipCreateForm" label-width="90px">
<el-form-item label="姓名" prop="name">
<el-input v-model="vipCreateForm.name" placeholder="请输入姓名"></el-input>
</el-form-item>
<el-form-item label="手机号" prop="mobile">
<el-input v-model="vipCreateForm.mobile" placeholder="请输入手机号"></el-input>
</el-form-item>
<el-form-item label="证件类型">
<el-select v-model="vipCreateForm.credent" style="width:100%">
<el-option v-for="item in credentList" :key="item.id" :label="item.value" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="证件号码">
<el-input v-model="vipCreateForm.idcard" placeholder="请输入证件号码"></el-input>
</el-form-item>
<el-form-item label="车牌号">
<el-input v-model="vipCreateForm.plate_no" placeholder="请输入车牌号"></el-input>
</el-form-item>
<el-form-item label="单位名称">
<el-input v-model="vipCreateForm.company_name" placeholder="请输入单位名称"></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="vipCreateVisible = false">取消</el-button>
<el-button type="primary" @click="submitVipCreate"></el-button>
</span>
</el-dialog>
</div>
</template>
@ -330,6 +373,10 @@
show,
save
} from "@/api/visit/record.js"
import {
getList as getVipList,
save as saveVip
} from "@/api/visit/vip.js"
import {
getparameter
} from "@/api/system/dictionary";
@ -350,7 +397,7 @@
type: 'add',
id: '',
visitType: "",
typeName: '',
typeName: '',
visitAreaText:"",
form: {
// type: 1,
@ -363,28 +410,49 @@
plate: "",
remark: "",
visitorinfo: "",
vip_source: "",
name: "",
mobile: "",
credent: 1,
idcard: "",
company_name: "",
company_name: "",
cda:'',
cars: [],
cars: [],
carsno:[],
follw_people: [],
long_time: 0,
long_time: 0,
longrange: "",
visitorinfos: "",
accpet_department_id: "",
accept_admin_id: "",
visitorinfos1:'',
visitorinfos: "",
accpet_department_id: "",
accept_admin_id: "",
visitorinfos1:'',
accompany_id:'',
accept_goods_admin_id: ""
},
followList:[],
followList:[],
carsList:[],
vipSelectId: '',
vipOptions: [],
vipLoading: false,
vipCreateVisible: false,
vipCreateForm: {
name: '',
mobile: '',
credent: 1,
idcard: '',
plate_no: '',
company_name: '',
status: 1,
},
vipCreateRules: {
name: [{ required: true, message: '请填写姓名', trigger: 'blur' }],
mobile: [
{ required: true, message: '请填写手机号', trigger: 'blur' },
{ pattern: /^[1][3-9][\d]{9}/, message: '手机号格式错误', trigger: 'blur' }
]
},
visitTimeList: [],
visitAreaList: [],
visitAreaList: [],
parkAreaList: [],
reasonList: [],
credentList: [{
@ -406,17 +474,17 @@
},
],
departmentList: [],
adminList: [],
carTable:[{
label: "车牌号",
prop: "car",
// width: 200,
customFn: (row, scope) => {
return (<el-input type="text" placeholder = "请填写车牌号"
v-model={row.car}>
</el-input>
)
}
adminList: [],
carTable:[{
label: "车牌号",
prop: "car",
// width: 200,
customFn: (row, scope) => {
return (<el-input type="text" placeholder = "请填写车牌号"
v-model={row.car}>
</el-input>
)
}
}],
followTable: [{
label: "姓名",
@ -497,10 +565,10 @@
visit_area_id: [{
required: true,
message: '请选择到访区域'
}],
plate: [{
required: true,
message: '请选择停车区域'
}],
plate: [{
required: true,
message: '请选择停车区域'
}],
name: [{
required: true,
@ -514,31 +582,31 @@
pattern: /^[1][3-9][\d]{9}/,
message: "手机号格式错误",
}
],
company_name: [{
required: true,
message: '请输入单位名称'
}],
cda:[{
required: true,
message: '请输入CDA编号'
}],
cars: [{
required: true,
message: '请输入车牌号'
}],
accompany_id: [{
required: true,
message: '请选择陪同人'
}],
accept_admin_id: [{
required: true,
message: '请选被访人'
}],
accept_goods_admin_id: [{
required: true,
message: '请选择收货人'
}]
],
company_name: [{
required: true,
message: '请输入单位名称'
}],
cda:[{
required: true,
message: '请输入CDA编号'
}],
cars: [{
required: true,
message: '请输入车牌号'
}],
accompany_id: [{
required: true,
message: '请选择陪同人'
}],
accept_admin_id: [{
required: true,
message: '请选被访人'
}],
accept_goods_admin_id: [{
required: true,
message: '请选择收货人'
}]
},
pickerOptions: {
@ -579,39 +647,40 @@
const res = await show({
id: this.id
})
this.form = {
visitinfo: "",
date: res?.date,
visit_time_id: res?.visit_time_id,
visit_area_id: res?.visit_area_id,
workRange: res.work_start_time?[res.work_start_time,res.work_end_time]:"",
reason: res?.reason,
plate: res?.plate,
remark: res?.remark,
visitorinfo: "",
name: res?.name,
mobile: res?.mobile,
credent: res?.credent,
idcard: res?.idcard,
company_name: res?.company_name,
cda:res?.cda,
cars:[],
carsno: [],
follw_people: res?.follw_people,
long_time: res?.long_time,
longrange: res.start_date?[res.start_date,res.end_date]:"",
visitorinfos: "",
accpet_department_id: res?.accpet_department_id,
accept_admin_id: res?.accept_admin_id,
visitorinfos1:'',
accompany_id:res?.accompany_id,
accept_goods_admin_id: res?parseInt(res.accept_goods_admin_id):''
}
this.followList = res?.follw_people
for(var k of res?.cars){
this.carsList.push({car:k})
}
this.visitAreaText = res?.visit_area.name
this.form = {
visitinfo: "",
date: res?.date,
visit_time_id: res?.visit_time_id,
visit_area_id: res?.visit_area_id,
workRange: res.work_start_time?[res.work_start_time,res.work_end_time]:"",
reason: res?.reason,
plate: res?.plate,
remark: res?.remark,
visitorinfo: "",
vip_source: "",
name: res?.name,
mobile: res?.mobile,
credent: res?.credent,
idcard: res?.idcard,
company_name: res?.company_name,
cda:res?.cda,
cars:[],
carsno: [],
follw_people: res?.follw_people,
long_time: res?.long_time,
longrange: res.start_date?[res.start_date,res.end_date]:"",
visitorinfos: "",
accpet_department_id: res?.accpet_department_id,
accept_admin_id: res?.accept_admin_id,
visitorinfos1:'',
accompany_id:res?.accompany_id,
accept_goods_admin_id: res?parseInt(res.accept_goods_admin_id):''
}
this.followList = res?.follw_people
for(var k of res?.cars){
this.carsList.push({car:k})
}
this.visitAreaText = res?.visit_area.name
console.log(this.followList,this.carsList)
},
async getLabel() {
@ -625,100 +694,159 @@
const reason = await getparameter({
number: "reasonList",
});
this.reasonList = reason?.detail;
const park = await getparameter({
number: "parkArea",
});
this.reasonList = reason?.detail;
const park = await getparameter({
number: "parkArea",
});
this.parkAreaList = park?.detail;
},
changeVisitArea(val){
console.log(val)
this.visitAreaList.map(item=>{
if(item.id==val){
this.visitAreaText = item.name
}
})
console.log(this.visitAreaText)
},
changelongrange(val){
this.form.start_date = val[0]
this.form.end_date = val[1]
},
changeworkrange(val){
this.form.work_start_time = val[0]
this.form.work_end_time = val[1]
},
changeAdmin(item){
if(item){
this.adminList.map(e=>{
if(e.id==item){
this.form.accompany_id = e.id
}
})
}
},
changeAdmin1(item){
if(item){
this.adminList.map(e=>{
if(e.id==item){
this.form.accept_admin_id = e.id
this.form.accpet_department_id = e.department_id
if(this.visitAreaText=='非生产区'){
this.form.accompany_id = e.id
}
}
})
}
},
changeGoods(item){
if(item){
this.adminList.map(e=>{
if(e.id==item){
this.form.accept_admin_id = e.id
this.form.accept_goods_admin_id = e.id
this.form.accpet_department_id = e.department_id
if(this.visitAreaText=='非生产区'){
this.form.accompany_id = e.id
}
}
})
}
},
async getVipSourceList(keyword = '') {
this.vipLoading = true
try {
const res = await getVipList({
page: 1,
page_size: 200,
keyword,
status: 1
})
this.vipOptions = res?.data || []
} finally {
this.vipLoading = false
}
},
fillFormByVip(vip) {
if (!vip) return
this.form.name = vip.name || ''
this.form.mobile = vip.mobile || ''
this.form.credent = vip.credent || 1
this.form.idcard = vip.idcard || ''
this.form.company_name = vip.company_name || ''
},
handleVipSelect(vipId) {
const target = this.vipOptions.find(item => item.id === vipId)
this.fillFormByVip(target)
},
openVipCreate() {
this.vipCreateVisible = true
this.$nextTick(() => {
if (this.$refs.vipCreateForm) {
this.$refs.vipCreateForm.resetFields()
}
this.vipCreateForm = {
name: '',
mobile: '',
credent: 1,
idcard: '',
plate_no: '',
company_name: '',
status: 1,
}
})
},
submitVipCreate() {
this.$refs.vipCreateForm.validate(async valid => {
if (!valid) return
await saveVip({
...this.vipCreateForm
})
this.$successMessage('add', 'VIP客户')
this.vipCreateVisible = false
await this.getVipSourceList(this.vipCreateForm.mobile)
const matched = this.vipOptions.find(item => item.mobile === this.vipCreateForm.mobile)
if (matched) {
this.vipSelectId = matched.id
this.fillFormByVip(matched)
}
})
},
changeVisitArea(val){
console.log(val)
this.visitAreaList.map(item=>{
if(item.id==val){
this.visitAreaText = item.name
}
})
console.log(this.visitAreaText)
},
changelongrange(val){
this.form.start_date = val[0]
this.form.end_date = val[1]
},
changeworkrange(val){
this.form.work_start_time = val[0]
this.form.work_end_time = val[1]
},
changeAdmin(item){
if(item){
this.adminList.map(e=>{
if(e.id==item){
this.form.accompany_id = e.id
}
})
}
},
changeAdmin1(item){
if(item){
this.adminList.map(e=>{
if(e.id==item){
this.form.accept_admin_id = e.id
this.form.accpet_department_id = e.department_id
if(this.visitAreaText=='非生产区'){
this.form.accompany_id = e.id
}
}
})
}
},
changeGoods(item){
if(item){
this.adminList.map(e=>{
if(e.id==item){
this.form.accept_admin_id = e.id
this.form.accept_goods_admin_id = e.id
this.form.accpet_department_id = e.department_id
if(this.visitAreaText=='非生产区'){
this.form.accompany_id = e.id
}
}
})
}
},
submit() {
let that = this
if(that.form.credent==1){
const regtest = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/
if(!regtest.test(that.form.idcard)){
this.$successMessage('身份证格式错误', '','warning')
return
}
}
if(that.form.credent==2){
const regtest = /^([a-zA-z]|[0-9]){5,17}$/
if(!regtest.test(that.form.idcard)){
this.$successMessage('护照格式错误', '','warning')
return
}
}
that.form.follw_people = that.followList
let _car = []
for(var k of that.carsList){
_car.push(k.car)
}
this.form.cars = _car
console.log("this.form.cars",this.form.cars)
// return
if(this.visitType==3){
this.form.accept_admin_id = this.form.accept_goods_admin_id
let that = this
if(that.form.credent==1){
const regtest = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/
if(!regtest.test(that.form.idcard)){
this.$successMessage('身份证格式错误', '','warning')
return
}
}
if(that.form.credent==2){
const regtest = /^([a-zA-z]|[0-9]){5,17}$/
if(!regtest.test(that.form.idcard)){
this.$successMessage('护照格式错误', '','warning')
return
}
}
that.form.follw_people = that.followList
let _car = []
for(var k of that.carsList){
_car.push(k.car)
}
this.form.cars = _car
console.log("this.form.cars",this.form.cars)
// return
if(this.visitType==3){
this.form.accept_admin_id = this.form.accept_goods_admin_id
}
if (this.type === 'editor') {
this.form.id = this.id
}else{
this.form.id = ""
}else{
this.form.id = ""
}
// return
save({
@ -732,10 +860,13 @@
},
watch: {
isShow(newVal) {
if (newVal) {
console.log("this.visitType",this.visitType)
this.form.type = this.visitType
if (newVal) {
console.log("this.visitType",this.visitType)
this.form.type = this.visitType
this.form.audit_status = 1
if (this.visitType == 4) {
this.getVipSourceList()
}
if (this.type === 'editor') {
this.getDetail()
}
@ -743,15 +874,18 @@
this.id = ''
this.type = ''
this.visitType = 1
this.followList = []
this.carsList = []
this.followList = []
this.carsList = []
this.vipSelectId = ''
this.vipOptions = []
this.vipCreateVisible = false
this.visitAreaText=''
this.$refs['dialog'].reset()
}
},
visitType(val) {
if (val) {
this.typeName = val == 1 ? '普通访客' : (val == 2 ? '施工访客' : (val == 3 ? '物流车辆' : ''))
this.typeName = val == 1 ? '普通访客' : (val == 2 ? '施工访客' : (val == 3 ? '物流车辆' : (val == 4 ? 'VIP访客' : '')))
}
}
@ -775,11 +909,11 @@
position: absolute;
top: 4px;
right: 4px;
}
.carsitem{
width:450px;margin-bottom:10px;margin-bottom: 10px;display: flex;align-items: center;justify-content: space-between;
}
.carsitem Button{
margin-left:10px
}
</style>
.carsitem{
width:450px;margin-bottom:10px;margin-bottom: 10px;display: flex;align-items: center;justify-content: space-between;
}
.carsitem Button{
margin-left:10px
}
</style>

@ -0,0 +1,179 @@
<template>
<div>
<xy-dialog ref="dialog" :is-show.sync="isShow" type="form" :title="type === 'add' ? '新增VIP客户' : '编辑VIP客户'" :form="form"
:rules="rules" @submit="submit">
<template v-slot:name>
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;">*</span>姓名
</div>
<div class="xy-table-item-content">
<el-input v-model="form.name" placeholder="请输入姓名" style="width:300px"></el-input>
</div>
</div>
</template>
<template v-slot:mobile>
<div class="xy-table-item">
<div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;">*</span>手机号
</div>
<div class="xy-table-item-content">
<el-input v-model="form.mobile" placeholder="请输入手机号" style="width:300px"></el-input>
</div>
</div>
</template>
<template v-slot:credent>
<div class="xy-table-item">
<div class="xy-table-item-label">证件类型</div>
<div class="xy-table-item-content">
<el-select v-model="form.credent" placeholder="请选择" style="width:300px">
<el-option v-for="item in credentList" :key="item.id" :label="item.value" :value="item.id"></el-option>
</el-select>
</div>
</div>
</template>
<template v-slot:idcard>
<div class="xy-table-item">
<div class="xy-table-item-label">证件号码</div>
<div class="xy-table-item-content">
<el-input v-model="form.idcard" placeholder="请输入证件号码" style="width:300px"></el-input>
</div>
</div>
</template>
<template v-slot:plate_no>
<div class="xy-table-item">
<div class="xy-table-item-label">车牌号</div>
<div class="xy-table-item-content">
<el-input v-model="form.plate_no" placeholder="请输入车牌号" style="width:300px"></el-input>
</div>
</div>
</template>
<template v-slot:company_name>
<div class="xy-table-item">
<div class="xy-table-item-label">单位名称</div>
<div class="xy-table-item-content">
<el-input v-model="form.company_name" placeholder="请输入单位名称" style="width:300px"></el-input>
</div>
</div>
</template>
<template v-slot:position>
<div class="xy-table-item">
<div class="xy-table-item-label">职位</div>
<div class="xy-table-item-content">
<el-input v-model="form.position" placeholder="请输入职位" style="width:300px"></el-input>
</div>
</div>
</template>
<template v-slot:status>
<div class="xy-table-item">
<div class="xy-table-item-label">状态</div>
<div class="xy-table-item-content">
<el-select v-model="form.status" placeholder="请选择" style="width:300px">
<el-option v-for="item in statusList" :key="item.id" :label="item.value" :value="item.id"></el-option>
</el-select>
</div>
</div>
</template>
<template v-slot:remark>
<div class="xy-table-item">
<div class="xy-table-item-label">备注</div>
<div class="xy-table-item-content">
<el-input type="textarea" v-model="form.remark" placeholder="请输入备注" style="width:300px"></el-input>
</div>
</div>
</template>
</xy-dialog>
</div>
</template>
<script>
import { show, save } from "@/api/visit/vip.js";
export default {
data() {
return {
isShow: false,
type: "add",
id: "",
form: {
name: "",
mobile: "",
credent: 1,
idcard: "",
plate_no: "",
company_name: "",
position: "",
status: 1,
remark: ""
},
statusList: [
{ id: 1, value: "启用" },
{ id: 2, value: "禁用" }
],
credentList: [
{ id: 1, value: "身份证" },
{ id: 2, value: "护照" }
],
rules: {
name: [{ required: true, message: "请填写姓名" }],
mobile: [
{ required: true, message: "请填写手机号" },
{ pattern: /^[1][3-9][\d]{9}/, message: "手机号格式错误" }
]
}
};
},
methods: {
async getDetail() {
const res = await show({ id: this.id });
this.form = {
name: res?.name || "",
mobile: res?.mobile || "",
credent: res?.credent || 1,
idcard: res?.idcard || "",
plate_no: res?.plate_no || "",
company_name: res?.company_name || "",
position: res?.position || "",
status: res?.status || 1,
remark: res?.remark || ""
};
},
submit() {
if (this.type === "editor") {
this.form.id = this.id;
} else {
this.form.id = "";
}
save({ ...this.form }).then(() => {
this.$successMessage(this.type, "VIP客户");
this.isShow = false;
this.$emit("refresh");
});
}
},
watch: {
isShow(newVal) {
if (newVal) {
if (this.type === "editor") {
this.getDetail();
}
} else {
this.id = "";
this.type = "";
this.$refs["dialog"].reset();
}
}
}
};
</script>
<style scoped lang="scss">
.xy-table-item-label {
width: 180px !important;
}
.xy-table-item-content {
width: 100%;
}
</style>

@ -8,6 +8,11 @@
<el-input size="mini" placeholder="请输入关键词" v-model="select.keyword"
style="width: 160px;margin-right: 10px;"></el-input>
<div style="margin-right: 10px;">访客类型</div>
<el-select v-model="select.type" clearable placeholder="全部" style="width: 130px;margin-right: 10px;">
<el-option v-for="item in typeList" :key="item.id" :label="item.value" :value="item.id">
</el-option>
</el-select>
<!-- <div style="margin-right: 10px;">状态</div>
<el-select v-model="select.audit_status" clearable placeholder="请选择">
<el-option v-for="item in statusList" :key="item.id" :label="item.value" :value="item.id">
@ -27,26 +32,114 @@
</lx-header>
</div>
<xy-table :table-item="table" :list="data" :total="total"
:auths="['edit','delete']"
<xy-table :table-item="table" :list="data" :total="total"
:auths="['view','edit','delete']"
@pageSizeChange="e => {select.page_size = e;select.page = 1;getList()}"
@pageIndexChange="e => {select.page = e;getList()}" @delete="deleteStudy" @editor="editorStudy">
@pageIndexChange="e => {select.page = e;getList()}" @delete="deleteStudy" @editor="editorStudy">
<template v-slot:view="scope">
<Button size="small" type="primary" ghost @click="viewDetail(scope.row)"></Button>
</template>
</xy-table>
<addCommon ref="addCommon" @refresh="getList"></addCommon>
<el-dialog title="长期访客详情" :visible.sync="viewDialogVisible" width="1080px">
<div v-if="viewDetailData">
<el-card shadow="never">
<div slot="header" style="font-weight:600;">
拜访信息
<el-tag size="mini" :type="viewDetailData.long_time == 1 ? 'warning' : 'info'" style="margin-left:8px;">
{{ viewDetailData.long_time == 1 ? '长期访客' : '普通时效' }}
</el-tag>
<el-tag size="mini" :type="statusTagType(viewDetailData.audit_status)" style="margin-left:8px;">
{{ viewDetailData.audit_status_text || '-' }}
</el-tag>
</div>
<div class="detail-grid">
<div class="detail-item"><div class="detail-label">访客类型</div><div class="detail-value">{{ viewDetailData.type_text || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">到访日期</div><div class="detail-value">{{ viewDetailData.date || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">到访时段</div><div class="detail-value">{{ visitTimeText(viewDetailData) }}</div></div>
<div class="detail-item"><div class="detail-label">前往区域</div><div class="detail-value">{{ viewDetailData.visit_area ? viewDetailData.visit_area.name : '-' }}</div></div>
<div class="detail-item"><div class="detail-label">到访事由</div><div class="detail-value">{{ viewDetailData.reason || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">施工时段</div><div class="detail-value">{{ workRangeText(viewDetailData) }}</div></div>
<div class="detail-item"><div class="detail-label">车辆类型</div><div class="detail-value">{{ viewDetailData.plate || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">长期开始</div><div class="detail-value">{{ viewDetailData.start_date || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">长期结束</div><div class="detail-value">{{ viewDetailData.end_date || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">二维码编号</div><div class="detail-value">{{ viewDetailData.code || '-' }}</div></div>
<div class="detail-item detail-item-full"><div class="detail-label">备注</div><div class="detail-value">{{ viewDetailData.remark || '-' }}</div></div>
</div>
</el-card>
<el-card shadow="never" style="margin-top:12px;">
<div slot="header" style="font-weight:600;">拜访人信息</div>
<div class="detail-grid">
<div class="detail-item"><div class="detail-label">姓名</div><div class="detail-value">{{ viewDetailData.name || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">联系电话</div><div class="detail-value">{{ viewDetailData.mobile || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">单位名称</div><div class="detail-value">{{ viewDetailData.company_name || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">证件类型</div><div class="detail-value">{{ credentText(viewDetailData.credent) }}</div></div>
<div class="detail-item"><div class="detail-label">证件号码</div><div class="detail-value">{{ viewDetailData.idcard || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">CDA编号</div><div class="detail-value">{{ viewDetailData.cda || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">到访车辆</div><div class="detail-value">{{ formatArray(viewDetailData.cars) }}</div></div>
<div class="detail-item"><div class="detail-label">随访人数</div><div class="detail-value">{{ followCount(viewDetailData.follw_people) }}</div></div>
<div class="detail-item"><div class="detail-label">创建时间</div><div class="detail-value">{{ viewDetailData.created_at || '-' }}</div></div>
</div>
</el-card>
</xy-table>
<addCommon ref="addCommon" @refresh="getList"></addCommon>
<el-card shadow="never" style="margin-top:12px;">
<div slot="header" style="font-weight:600;">被访与陪同信息</div>
<div class="detail-grid">
<div class="detail-item"><div class="detail-label">被访人</div><div class="detail-value">{{ viewDetailData.accept_admin ? viewDetailData.accept_admin.name : '-' }}</div></div>
<div class="detail-item"><div class="detail-label">陪同人</div><div class="detail-value">{{ viewDetailData.accompany ? viewDetailData.accompany.name : '-' }}</div></div>
<div class="detail-item"><div class="detail-label">收货人</div><div class="detail-value">{{ viewDetailData.accept_goods_admin ? viewDetailData.accept_goods_admin.name : '-' }}</div></div>
</div>
</el-card>
<el-card shadow="never" style="margin-top:12px;">
<div slot="header" style="font-weight:600;">随访人员信息</div>
<el-table v-if="Array.isArray(viewDetailData.follw_people) && viewDetailData.follw_people.length" :data="viewDetailData.follw_people" border size="mini">
<el-table-column prop="name" label="姓名" min-width="120"></el-table-column>
<el-table-column prop="mobile" label="联系电话" min-width="140"></el-table-column>
<el-table-column label="证件类型" min-width="120">
<template slot-scope="scope">{{ credentText(scope.row.credent) }}</template>
</el-table-column>
<el-table-column prop="idcard" label="证件号码" min-width="200"></el-table-column>
</el-table>
<div v-else class="empty-text">无随访人员</div>
</el-card>
<el-card shadow="never" style="margin-top:12px;">
<div slot="header" style="font-weight:600;">每日进出场记录</div>
<div v-if="!viewDetailData.daily_gate_records || !viewDetailData.daily_gate_records.length" class="empty-text"></div>
<el-collapse v-else>
<el-collapse-item v-for="item in viewDetailData.daily_gate_records" :key="item.biz_date" :title="item.biz_date">
<el-timeline>
<el-timeline-item v-for="record in item.records" :key="record.id" :timestamp="record.created_at" :type="record.action == 1 ? 'success' : 'info'">
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
<el-tag size="mini" :type="record.action == 1 ? 'success' : 'warning'">{{ record.action_text || '-' }}</el-tag>
<span>操作人{{ record.admin_name || '-' }}</span>
<span>人牌{{ formatArray(record.person_no) }}</span>
<span>车牌{{ formatArray(record.car_no) }}</span>
</div>
</el-timeline-item>
</el-timeline>
</el-collapse-item>
</el-collapse>
</el-card>
</div>
</el-dialog>
</div>
</template>
<script>
import {
getList
} from '@/api/visit/record.js'
import {
download
} from '@/utils/downloadRequest'
getList,
destroy,
show
} from '@/api/visit/record.js'
import {
download
} from '@/utils/downloadRequest'
import addCommon from '@/views/visit/component/addCommon'
export default {
components: {
components: {
addCommon
},
data() {
@ -56,12 +149,19 @@
page_size: 10,
keyword: "",
audit_status: "",
type: '',
start_date: "",
end_date: "",
is_export: 0,
is_export: 0,
long_time:1
},
selectRange: [],
typeList: [
{ id: 1, value: '普通访客' },
{ id: 2, value: '施工访客' },
{ id: 3, value: '物流车辆' },
{ id: 4, value: 'VIP访客' }
],
statusList: [{
id: -1,
value: '待学习'
@ -92,6 +192,8 @@
],
total: 0,
data: [],
viewDialogVisible: false,
viewDetailData: null,
table: [{
label: '序号',
type: "index",
@ -121,9 +223,9 @@
label: '是否随访',
sortable: false,
prop: 'follw_people',
width: 80,
formatter:(cell, data, value)=>{
return value?'是':'否'
width: 80,
formatter:(cell, data, value)=>{
return value?'是':'否'
}
},
{
@ -136,9 +238,9 @@
label: '证件类型',
sortable: false,
prop: 'credent',
width: 120,
formatter:(cell, data, value)=>{
return value==1?'身份证':'护照'
width: 120,
formatter:(cell, data, value)=>{
return value==1?'身份证':'护照'
},
},
{
@ -182,10 +284,10 @@
{
label: '创建人',
sortable: false,
prop: 'admin.name',
width: 120,
formatter(cell, data, value){
return value?value:''
prop: 'admin.name',
width: 120,
formatter(cell, data, value){
return value?value:''
}
}
]
@ -201,41 +303,83 @@
console.log(res)
this.data = res.data
this.total = res.total
},
selectRanges(val){
if(val){
this.select.start_date = val[0]
this.select.end_date = val[1]
}else{
this.select.start_date = ''
this.select.end_date = ''
}
},
downloadExel() {
this.select.is_export = 1
download(
'/api/admin/visit/index',
'get', {
...this.select
},
'拜访记录列表.xlsx')
this.select.is_export = 0
},
deleteStudy(row) {
destroy({
id: row.id
}).then(res => {
this.$successMessage('destroy', '长期访客')
this.getList()
})
},
editorStudy(row) {
// let addWhat = row.type == 1 ? "addCommon" : (row.type == 2 ? "addBuild" : (row.type == 3 ? "addPark" : ""))
let addWhat = "addCommon"
this.$refs[addWhat].id = row.id
this.$refs[addWhat].visitType=row.type
this.$refs[addWhat].type = 'editor'
this.$refs[addWhat].isShow = true
},
selectRanges(val){
if(val){
this.select.start_date = val[0]
this.select.end_date = val[1]
}else{
this.select.start_date = ''
this.select.end_date = ''
}
},
downloadExel() {
this.select.is_export = 1
download(
'/api/admin/visit/index',
'get', {
...this.select
},
'拜访记录列表.xlsx')
this.select.is_export = 0
},
deleteStudy(row) {
destroy({
id: row.id
}).then(res => {
this.$successMessage('destroy', '长期访客')
this.getList()
})
},
editorStudy(row) {
// let addWhat = row.type == 1 ? "addCommon" : (row.type == 2 ? "addBuild" : (row.type == 3 ? "addPark" : ""))
let addWhat = "addCommon"
this.$refs[addWhat].id = row.id
this.$refs[addWhat].visitType=row.type
this.$refs[addWhat].type = 'editor'
this.$refs[addWhat].isShow = true
},
async viewDetail(row) {
let res = await show({
id: row.id
})
this.viewDetailData = res
this.viewDialogVisible = true
},
credentText(credent) {
return parseInt(credent) === 2 ? '护照' : '身份证'
},
statusTagType(status) {
const map = {
'-1': 'info',
0: 'warning',
1: 'success',
2: 'danger',
3: 'success',
4: 'info',
5: ''
}
return map[status] || ''
},
visitTimeText(detail) {
if (!detail) return '-'
if (detail.visit_time && detail.visit_time.start_time) {
return `${detail.visit_time.start_time}-${detail.visit_time.end_time}`
}
return detail.visit_time_text || '-'
},
workRangeText(detail) {
if (!detail) return '-'
if (detail.work_start_time && detail.work_end_time) {
return `${detail.work_start_time}${detail.work_end_time}`
}
return '-'
},
followCount(list) {
return Array.isArray(list) ? list.length : 0
},
formatArray(val) {
return Array.isArray(val) && val.length ? val.join('、') : '-'
}
},
}
@ -275,4 +419,33 @@
line-height: 21px;
}
}
</style>
.detail-grid {
display: flex;
flex-wrap: wrap;
}
.detail-item {
width: 33.33%;
display: flex;
align-items: flex-start;
margin-bottom: 10px;
padding-right: 12px;
box-sizing: border-box;
}
.detail-item-full {
width: 100%;
}
.detail-label {
width: 110px;
color: #606266;
flex-shrink: 0;
}
.detail-value {
color: #303133;
word-break: break-all;
}
.empty-text {
text-align: center;
color: #909399;
padding: 20px 0;
}
</style>

@ -13,6 +13,11 @@
<el-option v-for="item in statusList" :key="item.id" :label="item.value" :value="item.id">
</el-option>
</el-select>
<div style="margin-right: 10px;">访客类型</div>
<el-select v-model="select.type" clearable placeholder="全部" style="width: 130px;margin-right: 10px;">
<el-option v-for="item in typeList" :key="item.id" :label="item.value" :value="item.id">
</el-option>
</el-select>
<div style="margin:0 10px;">起始时间</div>
<el-date-picker v-model="selectRange" @change="selectRanges" value-format="yyyy-MM-dd" type="daterange" range-separator=""
start-placeholder="开始日期" end-placeholder="结束日期">
@ -31,6 +36,8 @@
</el-button>
<el-button type="primary" size="mini"
@click="visible=false,$refs['addCommon'].isShow = true,$refs['addCommon'].type = 'add',$refs['addCommon'].visitType = 3">物流车辆</el-button>
<el-button type="primary" size="mini"
@click="visible=false,$refs['addCommon'].isShow = true,$refs['addCommon'].type = 'add',$refs['addCommon'].visitType = 4">VIP访客</el-button>
</div>
<el-button slot="reference" size="medium" type="primary" style="margin-left: 10px">新增</el-button>
</el-popover>
@ -43,12 +50,99 @@
</lx-header>
</div>
<xy-table :table-item="table" :list="data" :total="total"
:auths="['edit','delete']"
<xy-table :table-item="table" :list="data" :total="total"
:auths="['view','edit','delete']"
@pageSizeChange="e => {select.page_size = e;select.page = 1;getList()}"
@pageIndexChange="e => {select.page = e;getList()}" @delete="deleteStudy" @editor="editorStudy">
<template v-slot:view="scope">
<Button size="small" type="primary" ghost @click="viewDetail(scope.row)"></Button>
</template>
</xy-table>
<addCommon ref="addCommon" @refresh="getList"></addCommon>
<el-dialog title="拜访记录详情" :visible.sync="viewDialogVisible" width="1080px">
<div v-if="viewDetailData">
<el-card shadow="never">
<div slot="header" style="font-weight:600;">
拜访信息
<el-tag size="mini" :type="viewDetailData.long_time == 1 ? 'warning' : 'info'" style="margin-left:8px;">
{{ viewDetailData.long_time == 1 ? '长期访客' : '普通时效' }}
</el-tag>
<el-tag size="mini" :type="statusTagType(viewDetailData.audit_status)" style="margin-left:8px;">
{{ viewDetailData.audit_status_text || '-' }}
</el-tag>
</div>
<div class="detail-grid">
<div class="detail-item"><div class="detail-label">访客类型</div><div class="detail-value">{{ viewDetailData.type_text || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">到访日期</div><div class="detail-value">{{ viewDetailData.date || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">到访时段</div><div class="detail-value">{{ visitTimeText(viewDetailData) }}</div></div>
<div class="detail-item"><div class="detail-label">前往区域</div><div class="detail-value">{{ viewDetailData.visit_area ? viewDetailData.visit_area.name : '-' }}</div></div>
<div class="detail-item"><div class="detail-label">到访事由</div><div class="detail-value">{{ viewDetailData.reason || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">施工时段</div><div class="detail-value">{{ workRangeText(viewDetailData) }}</div></div>
<div class="detail-item"><div class="detail-label">车辆类型</div><div class="detail-value">{{ viewDetailData.plate || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">长期开始</div><div class="detail-value">{{ viewDetailData.start_date || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">长期结束</div><div class="detail-value">{{ viewDetailData.end_date || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">二维码编号</div><div class="detail-value">{{ viewDetailData.code || '-' }}</div></div>
<div class="detail-item detail-item-full"><div class="detail-label">备注</div><div class="detail-value">{{ viewDetailData.remark || '-' }}</div></div>
</div>
</el-card>
<el-card shadow="never" style="margin-top:12px;">
<div slot="header" style="font-weight:600;">拜访人信息</div>
<div class="detail-grid">
<div class="detail-item"><div class="detail-label">姓名</div><div class="detail-value">{{ viewDetailData.name || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">联系电话</div><div class="detail-value">{{ viewDetailData.mobile || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">单位名称</div><div class="detail-value">{{ viewDetailData.company_name || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">证件类型</div><div class="detail-value">{{ credentText(viewDetailData.credent) }}</div></div>
<div class="detail-item"><div class="detail-label">证件号码</div><div class="detail-value">{{ viewDetailData.idcard || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">CDA编号</div><div class="detail-value">{{ viewDetailData.cda || '-' }}</div></div>
<div class="detail-item"><div class="detail-label">到访车辆</div><div class="detail-value">{{ formatArray(viewDetailData.cars) }}</div></div>
<div class="detail-item"><div class="detail-label">随访人数</div><div class="detail-value">{{ followCount(viewDetailData.follw_people) }}</div></div>
<div class="detail-item"><div class="detail-label">创建时间</div><div class="detail-value">{{ viewDetailData.created_at || '-' }}</div></div>
</div>
</el-card>
<el-card shadow="never" style="margin-top:12px;">
<div slot="header" style="font-weight:600;">被访与陪同信息</div>
<div class="detail-grid">
<div class="detail-item"><div class="detail-label">被访人</div><div class="detail-value">{{ viewDetailData.accept_admin ? viewDetailData.accept_admin.name : '-' }}</div></div>
<div class="detail-item"><div class="detail-label">陪同人</div><div class="detail-value">{{ viewDetailData.accompany ? viewDetailData.accompany.name : '-' }}</div></div>
<div class="detail-item"><div class="detail-label">收货人</div><div class="detail-value">{{ viewDetailData.accept_goods_admin ? viewDetailData.accept_goods_admin.name : '-' }}</div></div>
</div>
</el-card>
<el-card shadow="never" style="margin-top:12px;">
<div slot="header" style="font-weight:600;">随访人员信息</div>
<el-table v-if="Array.isArray(viewDetailData.follw_people) && viewDetailData.follw_people.length" :data="viewDetailData.follw_people" border size="mini">
<el-table-column prop="name" label="姓名" min-width="120"></el-table-column>
<el-table-column prop="mobile" label="联系电话" min-width="140"></el-table-column>
<el-table-column label="证件类型" min-width="120">
<template slot-scope="scope">{{ credentText(scope.row.credent) }}</template>
</el-table-column>
<el-table-column prop="idcard" label="证件号码" min-width="200"></el-table-column>
</el-table>
<div v-else class="empty-text">无随访人员</div>
</el-card>
<el-card shadow="never" style="margin-top:12px;">
<div slot="header" style="font-weight:600;">每日进出场记录</div>
<div v-if="!viewDetailData.daily_gate_records || !viewDetailData.daily_gate_records.length" class="empty-text"></div>
<el-collapse v-else>
<el-collapse-item v-for="item in viewDetailData.daily_gate_records" :key="item.biz_date" :title="item.biz_date">
<el-timeline>
<el-timeline-item v-for="record in item.records" :key="record.id" :timestamp="record.created_at" :type="record.action == 1 ? 'success' : 'info'">
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
<el-tag size="mini" :type="record.action == 1 ? 'success' : 'warning'">{{ record.action_text || '-' }}</el-tag>
<span>操作人{{ record.admin_name || '-' }}</span>
<span>人牌{{ formatArray(record.person_no) }}</span>
<span>车牌{{ formatArray(record.car_no) }}</span>
</div>
</el-timeline-item>
</el-timeline>
</el-collapse-item>
</el-collapse>
</el-card>
</div>
</el-dialog>
<!-- <addBuild ref="addBuild" @refresh="getList"></addBuild>
<addPark ref="addPark" @refresh="getList"></addPark> -->
</div>
@ -57,12 +151,13 @@
<script>
import {
getList,
destroy
destroy,
show
} from '@/api/visit/record.js'
import addCommon from '@/views/visit/component/addCommon'
import {
download
} from '@/utils/downloadRequest'
import addCommon from '@/views/visit/component/addCommon'
import {
download
} from '@/utils/downloadRequest'
import store from "@/store/modules/user.js"
export default {
components: {
@ -75,13 +170,20 @@
page: 1,
page_size: 10,
keyword: "",
audit_status: "",
audit_status: "",
type: '',
my_accept_admin:'',
start_date: "",
end_date: "",
is_export: 0
},
selectRange: [],
typeList: [
{ id: 1, value: '普通访客' },
{ id: 2, value: '施工访客' },
{ id: 3, value: '物流车辆' },
{ id: 4, value: 'VIP访客' }
],
statusList: [{
id: -1,
value: '待学习'
@ -112,6 +214,8 @@
],
total: 0,
data: [],
viewDialogVisible: false,
viewDetailData: null,
table: [{
label: '序号',
type: "index",
@ -136,19 +240,19 @@
sortable: false,
prop: 'audit_status_text',
width: 120
},{
label: '被访人',
sortable: false,
prop: 'accept_admin.name',
width: 120,
},{
label: '被访人',
sortable: false,
prop: 'accept_admin.name',
width: 120,
},
{
label: '是否随访',
sortable: false,
prop: 'follw_people',
width: 80,
formatter:(cell, data, value)=>{
return value.length>0?'是':'否'
width: 80,
formatter:(cell, data, value)=>{
return value.length>0?'是':'否'
}
},
{
@ -161,9 +265,9 @@
label: '证件类型',
sortable: false,
prop: 'credent',
width: 120,
formatter:(cell, data, value)=>{
return value==1?'身份证':'护照'
width: 120,
formatter:(cell, data, value)=>{
return value==1?'身份证':'护照'
},
},
{
@ -184,7 +288,6 @@
prop: 'company_name',
width: 180
},
{
label: '开始时间',
sortable: false,
@ -196,40 +299,40 @@
sortable: false,
prop: 'end_date',
width: 180
},
{
label: '入场时间',
sortable: false,
prop: 'gate_start_date',
width: 180,
formatter:(cell,data,value)=>{
if(cell.gate_logs){
for(let k of cell.gate_logs){
if(k.remark=='进厂'){
return k.created_at
}
}
}else{
return ''
}
}
},
{
label: '离场时间',
sortable: false,
prop: 'gate_end_date',
width: 180,
formatter:(cell,data,value)=>{
if(cell.gate_logs){
for(let k of cell.gate_logs){
if(k.remark=='离厂'){
return k.created_at
}
}
}else{
return ''
}
}
},
{
label: '入场时间',
sortable: false,
prop: 'gate_start_date',
width: 180,
formatter:(cell,data,value)=>{
if(cell.gate_logs){
for(let k of cell.gate_logs){
if(k.remark=='进厂'){
return k.created_at
}
}
}else{
return ''
}
}
},
{
label: '离场时间',
sortable: false,
prop: 'gate_end_date',
width: 180,
formatter:(cell,data,value)=>{
if(cell.gate_logs){
for(let k of cell.gate_logs){
if(k.remark=='离厂'){
return k.created_at
}
}
}else{
return ''
}
}
},
{
label: '创建时间',
@ -242,20 +345,20 @@
label: '创建人',
sortable: false,
prop: 'admin.name',
width: 120,
formatter(cell, data, value){
return value?value:''
width: 120,
formatter(cell, data, value){
return value?value:''
}
}
]
}
},
computed: {},
mounted() {
console.log("stote",store)
// const state = store.state
// if(state.role && state.role.includes('')!=-1){
// this.select.my_accept_admin = ''
mounted() {
console.log("stote",store)
// const state = store.state
// if(state.role && state.role.includes('')!=-1){
// this.select.my_accept_admin = ''
// }
this.getList()
},
@ -265,25 +368,25 @@
console.log(res)
this.data = res.data
this.total = res.total
},
selectRanges(val){
if(val){
this.select.start_date = val[0]
this.select.end_date = val[1]
}else{
this.select.start_date = ''
this.select.end_date = ''
}
},
downloadExel() {
this.select.is_export = 1
download(
'/api/admin/visit/index',
'get', {
...this.select
},
'拜访记录列表.csv')
this.select.is_export = 0
},
selectRanges(val){
if(val){
this.select.start_date = val[0]
this.select.end_date = val[1]
}else{
this.select.start_date = ''
this.select.end_date = ''
}
},
downloadExel() {
this.select.is_export = 1
download(
'/api/admin/visit/index',
'get', {
...this.select
},
'拜访记录列表.csv')
this.select.is_export = 0
},
deleteStudy(row) {
destroy({
@ -295,12 +398,54 @@
},
editorStudy(row) {
// let addWhat = row.type == 1 ? "addCommon" : (row.type == 2 ? "addBuild" : (row.type == 3 ? "addPark" : ""))
let addWhat = "addCommon"
console.log(row)
this.$refs[addWhat].id = row.id
let addWhat = "addCommon"
console.log(row)
this.$refs[addWhat].id = row.id
this.$refs[addWhat].visitType= parseInt(row.type)
this.$refs[addWhat].type = 'editor'
this.$refs[addWhat].isShow = true
},
async viewDetail(row) {
let res = await show({
id: row.id
})
this.viewDetailData = res
this.viewDialogVisible = true
},
credentText(credent) {
return parseInt(credent) === 2 ? '护照' : '身份证'
},
statusTagType(status) {
const map = {
'-1': 'info',
0: 'warning',
1: 'success',
2: 'danger',
3: 'success',
4: 'info',
5: ''
}
return map[status] || ''
},
visitTimeText(detail) {
if (!detail) return '-'
if (detail.visit_time && detail.visit_time.start_time) {
return `${detail.visit_time.start_time}-${detail.visit_time.end_time}`
}
return detail.visit_time_text || '-'
},
workRangeText(detail) {
if (!detail) return '-'
if (detail.work_start_time && detail.work_end_time) {
return `${detail.work_start_time}${detail.work_end_time}`
}
return '-'
},
followCount(list) {
return Array.isArray(list) ? list.length : 0
},
formatArray(val) {
return Array.isArray(val) && val.length ? val.join('、') : '-'
}
},
}
@ -340,4 +485,33 @@
line-height: 21px;
}
}
</style>
.detail-grid {
display: flex;
flex-wrap: wrap;
}
.detail-item {
width: 33.33%;
display: flex;
align-items: flex-start;
margin-bottom: 10px;
padding-right: 12px;
box-sizing: border-box;
}
.detail-item-full {
width: 100%;
}
.detail-label {
width: 110px;
color: #606266;
flex-shrink: 0;
}
.detail-value {
color: #303133;
word-break: break-all;
}
.empty-text {
text-align: center;
color: #909399;
padding: 20px 0;
}
</style>

@ -0,0 +1,222 @@
<template>
<div style="padding: 0 20px">
<div ref="lxHeader">
<lx-header icon="md-apps" text="VIP客户管理" style="margin-bottom: 10px; border: 0px; margin-top: 15px">
<slot>
<div style="display: flex;align-items: center;">
<div style="margin-right: 10px;">关键词</div>
<el-input size="mini" placeholder="请输入姓名/手机号/单位/证件号/车牌号" v-model="select.keyword" style="width: 260px;margin-right: 10px;"></el-input>
<div style="margin-right: 10px;">状态</div>
<el-select size="mini" v-model="select.status" clearable placeholder="请选择" style="width:120px">
<el-option v-for="item in statusList" :key="item.id" :label="item.value" :value="item.id"></el-option>
</el-select>
<div style="margin:0 10px;">证件类型</div>
<el-select size="mini" v-model="select.credent" clearable placeholder="请选择" style="width:120px">
<el-option :value="1" label="身份证"></el-option>
<el-option :value="2" label="护照"></el-option>
</el-select>
<el-button @click="getList" size="medium" type="primary" style="margin-left: 10px">查询</el-button>
<el-button @click="$refs['addVip'].isShow = true;$refs['addVip'].type = 'add'" size="medium" type="primary" style="margin-left: 10px">新增</el-button>
<el-button @click="openImportDialog" size="medium" type="primary" style="margin-left: 10px">导入</el-button>
<el-button @click="downloadExel" size="medium" type="primary" style="margin-left: 10px">导出</el-button>
</div>
</slot>
</lx-header>
</div>
<xy-table :table-item="table" :list="data" :total="total" :auths="['edit','delete']" @pageSizeChange="e => {select.page_size = e;select.page = 1;getList()}" @pageIndexChange="e => {select.page = e;getList()}" @delete="deleteRow" @editor="editorRow">
</xy-table>
<addVip ref="addVip" @refresh="getList"></addVip>
<el-dialog title="导入VIP客户XLSX" :visible.sync="importDialogVisible" width="920px">
<div style="margin-bottom: 10px;display:flex;gap:10px;align-items:center;">
<el-button size="mini" type="primary" plain @click="downloadTemplate"></el-button>
<el-upload action="#" :auto-upload="false" :show-file-list="false" accept=".xlsx" :before-upload="handleXlsxPreview">
<el-button size="mini" type="primary">选择XLSX并预览</el-button>
</el-upload>
<span style="color:#999;">仅支持 `.xlsx`请先预览后再确认导入</span>
</div>
<div v-if="missingHeaders.length" style="margin-bottom:10px;color:#f56c6c;">
模板字段缺失{{ missingHeaders.join("、") }}请下载模板后重试
</div>
<el-table v-if="previewRows.length" :data="previewRows" border max-height="360">
<el-table-column prop="姓名" label="姓名" min-width="120"></el-table-column>
<el-table-column prop="手机号" label="手机号" min-width="140"></el-table-column>
<el-table-column prop="证件类型" label="证件类型" min-width="110"></el-table-column>
<el-table-column prop="证件号码" label="证件号码" min-width="160"></el-table-column>
<el-table-column prop="车牌号" label="车牌号" min-width="120"></el-table-column>
<el-table-column prop="单位名称" label="单位名称" min-width="160"></el-table-column>
<el-table-column prop="职位" label="职位" min-width="120"></el-table-column>
<el-table-column prop="状态" label="状态" min-width="100"></el-table-column>
</el-table>
<div v-else style="color:#999;">请先选择xlsx文件预览数据</div>
<span slot="footer">
<el-button @click="importDialogVisible = false">取消</el-button>
<el-button type="primary" :disabled="!importFile || missingHeaders.length > 0" @click="submitImport"></el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { getList, destroy } from "@/api/visit/vip.js";
import addVip from "@/views/visit/component/addVip";
import { download } from "@/utils/downloadRequest";
import { getToken } from "@/utils/auth";
import * as XLSX from "xlsx";
import axios from "axios";
export default {
components: { addVip },
data() {
return {
select: {
page: 1,
page_size: 10,
keyword: "",
status: "",
credent: "",
is_export: 0
},
total: 0,
data: [],
statusList: [
{ id: 1, value: "启用" },
{ id: 2, value: "禁用" }
],
importDialogVisible: false,
importFile: null,
previewRows: [],
missingHeaders: [],
requiredHeaders: ["姓名", "手机号"],
table: [
{ label: "序号", type: "index", fixed: "left" },
{ label: "姓名", prop: "name", width: 120, fixed: "left" },
{ label: "手机号", prop: "mobile", width: 150 },
{
label: "证件类型",
prop: "credent",
width: 120,
customFn: row => (row.credent === 1 ? "身份证" : "护照")
},
{ label: "证件号码", prop: "idcard", width: 180 },
{ label: "车牌号", prop: "plate_no", width: 140 },
{ label: "单位名称", prop: "company_name", width: 180 },
{ label: "职位", prop: "position", width: 120 },
{
label: "状态",
prop: "status",
width: 100,
customFn: row => (row.status === 1 ? <div style="color: green">启用</div> : <div style="color: red">禁用</div>)
},
{ label: "备注", prop: "remark", width: 220 },
{ label: "创建时间", prop: "created_at", width: 180 }
]
};
},
mounted() {
this.getList();
},
methods: {
async getList() {
const res = await getList(this.select);
this.data = res.data;
this.total = res.total;
},
downloadExel() {
this.select.is_export = 1;
download("/api/admin/vip-customer/index", "get", { ...this.select }, "VIP客户列表.xlsx");
this.select.is_export = 0;
},
openImportDialog() {
this.importDialogVisible = true;
this.importFile = null;
this.previewRows = [];
this.missingHeaders = [];
},
downloadTemplate() {
const templateRows = [
{
姓名: "张三",
手机号: "13800000000",
证件类型: "身份证",
证件号码: "320000199001010000",
车牌号: "苏B12345",
单位名称: "示例公司",
职位: "总监",
状态: "启用",
备注: "示例数据"
}
];
const ws = XLSX.utils.json_to_sheet(templateRows);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "VIP模板");
XLSX.writeFile(wb, "VIP客户导入模板.xlsx");
},
handleXlsxPreview(file) {
const isXlsx = /\.xlsx$/i.test(file.name);
if (!isXlsx) {
this.$successMessage("仅支持xlsx格式", "", "warning");
return false;
}
this.importFile = file;
const reader = new FileReader();
reader.onload = e => {
const data = new Uint8Array(e.target.result);
const workbook = XLSX.read(data, { type: "array" });
const sheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[sheetName];
const rows = XLSX.utils.sheet_to_json(worksheet, { defval: "" });
const headers = rows.length ? Object.keys(rows[0]) : [];
this.missingHeaders = this.requiredHeaders.filter(h => !headers.includes(h));
if (this.missingHeaders.length) {
this.previewRows = [];
this.$successMessage(`模板字段缺失:${this.missingHeaders.join("、")}`, "", "warning");
return;
}
this.previewRows = rows.slice(0, 200);
};
reader.readAsArrayBuffer(file);
return false;
},
async submitImport() {
if (!this.importFile) {
this.$successMessage("请先选择xlsx文件", "", "warning");
return;
}
const formData = new FormData();
formData.append("file", this.importFile);
try {
await axios.post(`${process.env.VUE_APP_BASE_API}/api/admin/vip-customer/import`, formData, {
headers: {
Authorization: `Bearer ${getToken()}`,
"Content-Type": "multipart/form-data"
}
});
this.$successMessage("导入成功", "");
this.importDialogVisible = false;
this.getList();
} catch (error) {
this.$successMessage("导入失败", "", "warning");
}
},
deleteRow(row) {
destroy({ id: row.id }).then(() => {
this.$successMessage("destroy", "VIP客户");
this.getList();
});
},
editorRow(row) {
this.$refs["addVip"].id = row.id;
this.$refs["addVip"].type = "editor";
this.$refs["addVip"].isShow = true;
}
}
};
</script>
<style scoped lang="scss">
::v-deep .el-button {
padding: 5px 8px !important;
}
</style>

@ -25,7 +25,7 @@ module.exports = {
* Detail: https://cli.vuejs.org/config/#publicpath
*/
publicPath: '/admin/',
outputDir: '/Users/mac/Documents/朗业/2023/b-bd智能访客系统/bd-fangke/public/admin',
outputDir: path.join(__dirname, '../yxbd-fangke/public/admin'),
assetsDir: 'static',
css: {
loaderOptions: { // 向 CSS 相关的 loader 传递选项
@ -34,7 +34,8 @@ module.exports = {
}
}
},
lintOnSave: process.env.NODE_ENV === 'development',
// Disable lint blocking in local dev so legacy codebase can run.
lintOnSave: false,
productionSourceMap: false,
devServer: {
port: port,

Loading…
Cancel
Save