Compare commits
20 Commits
1d732fb6d1
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7e6c05152 | ||
|
|
9132824c61 | ||
|
|
90e923f490 | ||
|
|
0b0dd5c8e8 | ||
|
|
802d994cfe | ||
|
|
664f49d9e0 | ||
|
|
9fd1ebc65e | ||
|
|
639c2c913d | ||
|
|
ffe1ed9e88 | ||
|
|
f090aeb9b9 | ||
|
|
d8caa6c7e6 | ||
|
|
febbd54134 | ||
|
|
cc60180990 | ||
|
|
a88b724f2f | ||
|
|
7baee5faff | ||
|
|
6ebe0b8b00 | ||
|
|
34a2b67695 | ||
|
|
fa5872b7df | ||
|
|
d0b847ab68 | ||
|
|
cc89340394 |
52
src/api/health/chronicDisease.js
Normal file
52
src/api/health/chronicDisease.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询慢性疾病档案列表
|
||||
export function listChronicDisease(query) {
|
||||
return request({
|
||||
url: '/health/chronicDisease/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 根据成员ID查询慢性疾病档案列表
|
||||
export function listChronicDiseaseByMember(personId) {
|
||||
return request({
|
||||
url: '/health/chronicDisease/listByPerson/' + personId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询慢性疾病档案详细
|
||||
export function getChronicDisease(id) {
|
||||
return request({
|
||||
url: '/health/chronicDisease/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增慢性疾病档案
|
||||
export function addChronicDisease(data) {
|
||||
return request({
|
||||
url: '/health/chronicDisease',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改慢性疾病档案
|
||||
export function updateChronicDisease(data) {
|
||||
return request({
|
||||
url: '/health/chronicDisease',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除慢性疾病档案
|
||||
export function delChronicDisease(id) {
|
||||
return request({
|
||||
url: '/health/chronicDisease/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
88
src/api/health/chronicDiseaseManage.js
Normal file
88
src/api/health/chronicDiseaseManage.js
Normal file
@@ -0,0 +1,88 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询慢病指标记录列表
|
||||
export function listIndicator(query) {
|
||||
return request({
|
||||
url: '/health/chronicDiseaseManage/indicator/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询慢病指标记录详细
|
||||
export function getIndicator(id) {
|
||||
return request({
|
||||
url: '/health/chronicDiseaseManage/indicator/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增慢病指标记录
|
||||
export function addIndicator(data) {
|
||||
return request({
|
||||
url: '/health/chronicDiseaseManage/indicator',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改慢病指标记录
|
||||
export function updateIndicator(data) {
|
||||
return request({
|
||||
url: '/health/chronicDiseaseManage/indicator',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除慢病指标记录
|
||||
export function delIndicator(id) {
|
||||
return request({
|
||||
url: '/health/chronicDiseaseManage/indicator/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询复诊随访记录列表
|
||||
export function listFollowUp(query) {
|
||||
return request({
|
||||
url: '/health/chronicDiseaseManage/followUp/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 新增复诊随访记录
|
||||
export function addFollowUp(data) {
|
||||
return request({
|
||||
url: '/health/chronicDiseaseManage/followUp',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改复诊随访记录
|
||||
export function updateFollowUp(data) {
|
||||
return request({
|
||||
url: '/health/chronicDiseaseManage/followUp',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除复诊随访记录
|
||||
export function delFollowUp(id) {
|
||||
return request({
|
||||
url: '/health/chronicDiseaseManage/followUp/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询状态变更历史
|
||||
export function listStatusHistory(query) {
|
||||
return request({
|
||||
url: '/health/chronicDiseaseManage/statusHistory/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
46
src/api/health/medicationAdherence.js
Normal file
46
src/api/health/medicationAdherence.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获取总体依从性统计
|
||||
export function getOverallAdherence(query) {
|
||||
return request({
|
||||
url: '/health/adherence/overall',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 获取每日依从性统计列表
|
||||
export function getDailyAdherence(query) {
|
||||
return request({
|
||||
url: '/health/adherence/daily',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 获取日历视图数据
|
||||
export function getCalendarData(memberId, yearMonth) {
|
||||
return request({
|
||||
url: '/health/adherence/calendar',
|
||||
method: 'get',
|
||||
params: { memberId, yearMonth }
|
||||
})
|
||||
}
|
||||
|
||||
// 获取各时段服药统计
|
||||
export function getTimeSlotAdherence(query) {
|
||||
return request({
|
||||
url: '/health/adherence/timeSlot',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 获取仪表盘概览数据
|
||||
export function getDashboard(memberId) {
|
||||
return request({
|
||||
url: '/health/adherence/dashboard',
|
||||
method: 'get',
|
||||
params: { memberId }
|
||||
})
|
||||
}
|
||||
68
src/api/health/medicationPlan.js
Normal file
68
src/api/health/medicationPlan.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询用药计划列表
|
||||
export function listMedicationPlan(query) {
|
||||
return request({
|
||||
url: '/health/medicationPlan/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询用药计划详细
|
||||
export function getMedicationPlan(id) {
|
||||
return request({
|
||||
url: '/health/medicationPlan/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增用药计划
|
||||
export function addMedicationPlan(data) {
|
||||
return request({
|
||||
url: '/health/medicationPlan',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改用药计划
|
||||
export function updateMedicationPlan(data) {
|
||||
return request({
|
||||
url: '/health/medicationPlan',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除用药计划
|
||||
export function delMedicationPlan(id) {
|
||||
return request({
|
||||
url: '/health/medicationPlan/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 暂停用药计划
|
||||
export function pauseMedicationPlan(id) {
|
||||
return request({
|
||||
url: '/health/medicationPlan/pause/' + id,
|
||||
method: 'put'
|
||||
})
|
||||
}
|
||||
|
||||
// 恢复用药计划
|
||||
export function resumeMedicationPlan(id) {
|
||||
return request({
|
||||
url: '/health/medicationPlan/resume/' + id,
|
||||
method: 'put'
|
||||
})
|
||||
}
|
||||
|
||||
// 结束用药计划
|
||||
export function endMedicationPlan(id) {
|
||||
return request({
|
||||
url: '/health/medicationPlan/end/' + id,
|
||||
method: 'put'
|
||||
})
|
||||
}
|
||||
53
src/api/health/medicationTask.js
Normal file
53
src/api/health/medicationTask.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询服药任务列表
|
||||
export function listMedicationTask(query) {
|
||||
return request({
|
||||
url: '/health/medicationTask/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询服药任务详细
|
||||
export function getMedicationTask(id) {
|
||||
return request({
|
||||
url: '/health/medicationTask/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除服药任务
|
||||
export function delMedicationTask(id) {
|
||||
return request({
|
||||
url: '/health/medicationTask/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 确认服药
|
||||
export function confirmMedication(id, confirmType, confirmTime) {
|
||||
return request({
|
||||
url: '/health/medicationTask/confirm/' + id,
|
||||
method: 'put',
|
||||
params: { confirmType, confirmTime }
|
||||
})
|
||||
}
|
||||
|
||||
// 跳过服药
|
||||
export function skipMedication(id, notes) {
|
||||
return request({
|
||||
url: '/health/medicationTask/skip/' + id,
|
||||
method: 'put',
|
||||
params: { notes }
|
||||
})
|
||||
}
|
||||
|
||||
// 标记漏服
|
||||
export function markMissed(id, notes) {
|
||||
return request({
|
||||
url: '/health/medicationTask/missed/' + id,
|
||||
method: 'put',
|
||||
params: { notes }
|
||||
})
|
||||
}
|
||||
@@ -24,6 +24,22 @@ export function getHealthAnalysis(query) {
|
||||
})
|
||||
}
|
||||
|
||||
export function getActivityAnalysis(query) {
|
||||
return request({
|
||||
url: '/health/analysis/activityAnalysis',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getHealthScreen(query) {
|
||||
return request({
|
||||
url: '/health/analysis/healthScreen',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getRecordAnalysis(query) {
|
||||
return request({
|
||||
url: '/health/analysis/recordAnalysis',
|
||||
@@ -32,6 +48,14 @@ export function getRecordAnalysis(query) {
|
||||
})
|
||||
}
|
||||
|
||||
export function getRecordScreen(query) {
|
||||
return request({
|
||||
url: '/health/analysis/recordScreen',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getDoctorAnalysis(query) {
|
||||
return request({
|
||||
url: '/health/analysis/doctorAnalysis',
|
||||
|
||||
@@ -121,3 +121,30 @@ export function getBankCardStatistics() {
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询交易地点消费地图概览
|
||||
export function getAccountDealLocationOverview(query) {
|
||||
return request({
|
||||
url: '/invest/analysis/accountDealLocationOverview',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询交易地点消费地图聚合点位
|
||||
export function getAccountDealLocationPoints(query) {
|
||||
return request({
|
||||
url: '/invest/analysis/accountDealLocationPoints',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询交易地点消费明细
|
||||
export function getAccountDealLocationRecords(query) {
|
||||
return request({
|
||||
url: '/invest/analysis/accountDealLocationRecords',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
53
src/api/system/feedback.js
Normal file
53
src/api/system/feedback.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询意见反馈列表
|
||||
export function listFeedback(query) {
|
||||
return request({
|
||||
url: '/system/feedback/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询意见反馈详细
|
||||
export function getFeedback(id) {
|
||||
return request({
|
||||
url: '/system/feedback/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增意见反馈
|
||||
export function addFeedback(data) {
|
||||
return request({
|
||||
url: '/system/feedback',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改意见反馈
|
||||
export function updateFeedback(data) {
|
||||
return request({
|
||||
url: '/system/feedback',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 回复意见反馈
|
||||
export function replyFeedback(data) {
|
||||
return request({
|
||||
url: '/system/feedback/reply',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除意见反馈
|
||||
export function delFeedback(id) {
|
||||
return request({
|
||||
url: '/system/feedback/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
@@ -158,11 +158,14 @@ function uploadedSuccessfully() {
|
||||
|
||||
// 获取文件名称
|
||||
function getFileName(name) {
|
||||
if (name.lastIndexOf('/') > -1) {
|
||||
return name.slice(name.lastIndexOf('/') + 1)
|
||||
} else {
|
||||
const fileName = String(name || '').split('?')[0]
|
||||
if (!fileName) {
|
||||
return ''
|
||||
}
|
||||
if (fileName.lastIndexOf('/') > -1) {
|
||||
return decodeURIComponent(fileName.slice(fileName.lastIndexOf('/') + 1))
|
||||
}
|
||||
return decodeURIComponent(fileName)
|
||||
}
|
||||
|
||||
// 对象转成指定字符串分隔
|
||||
|
||||
@@ -65,28 +65,19 @@ export const constantRoutes = [
|
||||
{
|
||||
path: '',
|
||||
component: Layout,
|
||||
redirect: '/accountCalendar'
|
||||
// children: [
|
||||
// {
|
||||
// path: '/index',
|
||||
// component: () => import('@/views/index'),
|
||||
// name: 'Index',
|
||||
// meta: { title: '首页', icon: 'dashboard', affix: true }
|
||||
// }
|
||||
// ]
|
||||
redirect: '/index'
|
||||
},
|
||||
{
|
||||
path: '/index',
|
||||
component: Layout,
|
||||
redirect: '/statisticanalysis/accountAnalysis'
|
||||
// children: [
|
||||
// {
|
||||
// path: '/index',
|
||||
// component: () => import('@/views/index'),
|
||||
// name: 'Index',
|
||||
// meta: { title: '首页', icon: 'dashboard', affix: true }
|
||||
// }
|
||||
// ]
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/index'),
|
||||
name: 'Index',
|
||||
meta: { title: '首页', icon: 'dashboard', affix: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/user',
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* 判断url是否是http或https
|
||||
* 判断url是否是http或https
|
||||
* @param {string} path
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isHttp(url) {
|
||||
export function isHttp(url) {
|
||||
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* @param {string} path
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isExternal(path) {
|
||||
export function isExternal(path) {
|
||||
return /^(https?:|mailto:|tel:)/.test(path)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ export function validUsername(str) {
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validURL(url) {
|
||||
const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
|
||||
const reg =
|
||||
/^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
|
||||
return reg.test(url)
|
||||
}
|
||||
|
||||
@@ -66,7 +67,8 @@ export function validAlphabets(str) {
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validEmail(email) {
|
||||
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||
const reg =
|
||||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||
return reg.test(email)
|
||||
}
|
||||
|
||||
@@ -91,3 +93,65 @@ export function isArray(arg) {
|
||||
}
|
||||
return Array.isArray(arg)
|
||||
}
|
||||
|
||||
// ============ 密码复杂度校验 ============
|
||||
// 规则(与 H5 端保持一致):
|
||||
// 1. 长度 8-20 个字符
|
||||
// 2. 不能包含空格
|
||||
// 3. 必须同时包含:大写字母、小写字母、数字、特殊字符 四类
|
||||
|
||||
export const PASSWORD_MIN_LENGTH = 8
|
||||
export const PASSWORD_MAX_LENGTH = 20
|
||||
|
||||
const REG_UPPER = /[A-Z]/
|
||||
const REG_LOWER = /[a-z]/
|
||||
const REG_NUMBER = /[0-9]/
|
||||
// 特殊字符:非字母、非数字、非空白(即标点符号等)
|
||||
const REG_SPECIAL = /[^A-Za-z0-9\s]/
|
||||
|
||||
/**
|
||||
* 校验密码复杂度,返回精确的失败原因
|
||||
* @param {string} password
|
||||
* @returns {{ valid: boolean, message: string }}
|
||||
*/
|
||||
export function validatePassword(password) {
|
||||
if (password === undefined || password === null || password === '') {
|
||||
return { valid: false, message: '密码不能为空' }
|
||||
}
|
||||
const value = String(password)
|
||||
if (value.length < PASSWORD_MIN_LENGTH || value.length > PASSWORD_MAX_LENGTH) {
|
||||
return { valid: false, message: `密码长度在 ${PASSWORD_MIN_LENGTH} 到 ${PASSWORD_MAX_LENGTH} 个字符` }
|
||||
}
|
||||
if (/\s/.test(value)) {
|
||||
return { valid: false, message: '密码不能包含空格' }
|
||||
}
|
||||
if (!REG_UPPER.test(value)) {
|
||||
return { valid: false, message: '密码必须包含大写字母' }
|
||||
}
|
||||
if (!REG_LOWER.test(value)) {
|
||||
return { valid: false, message: '密码必须包含小写字母' }
|
||||
}
|
||||
if (!REG_NUMBER.test(value)) {
|
||||
return { valid: false, message: '密码必须包含数字' }
|
||||
}
|
||||
if (!REG_SPECIAL.test(value)) {
|
||||
return { valid: false, message: '密码必须包含特殊字符' }
|
||||
}
|
||||
return { valid: true, message: '' }
|
||||
}
|
||||
|
||||
/**
|
||||
* Element Plus (async-validator) 自定义校验函数
|
||||
* 用法:{ validator: passwordValidator, trigger: 'blur' }
|
||||
* @param {object} rule
|
||||
* @param {string} value
|
||||
* @param {Function} callback
|
||||
*/
|
||||
export function passwordValidator(rule, value, callback) {
|
||||
const { valid, message } = validatePassword(value)
|
||||
if (valid) {
|
||||
callback()
|
||||
} else {
|
||||
callback(new Error(message))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
<el-form-item label="发行年份" prop="issueYear">
|
||||
<el-date-picker v-model="queryParams.issueYear" type="year" placeholder="请选择发行年份" value-format="YYYY" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="系列" prop="series">
|
||||
<!-- <el-form-item label="系列" prop="series">
|
||||
<el-select v-model="queryParams.series" placeholder="请选择系列" clearable>
|
||||
<el-option v-for="dict in commemorative_banknote_series" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
</el-form>
|
||||
<div class="search-btn-con">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
@@ -35,7 +35,7 @@
|
||||
<div class="content-con" v-loading="loading">
|
||||
<el-table v-loading="loading" :data="CommemorativeBanknoteList" @selection-change="handleSelectionChange" height="calc(100% - 0.62rem)">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="缩略图" align="center" prop="collectFileList" width="150">
|
||||
<el-table-column label="缩略图" align="center" prop="collectFileList" width="180">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
v-if="scope.row.collectFileList && scope.row.collectFileList.length > 0"
|
||||
@@ -58,13 +58,13 @@
|
||||
<dict-tag :options="commemorative_banknote_composition" :value="scope.row.composition" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="系列" align="center" prop="series">
|
||||
<!-- <el-table-column label="系列" align="center" prop="series">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="commemorative_banknote_series" :value="scope.row.series" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column> -->
|
||||
<!-- <el-table-column label="铸币单位" align="center" prop="coinageUnit" /> -->
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<el-table-column label="操作" width="220" align="center" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<div class="ctrl-btn d-flex">
|
||||
<el-tooltip v-for="item in operateList" :key="item.id" class="item" effect="dark" :content="item.title" placement="top">
|
||||
@@ -196,6 +196,7 @@ const previewImageList = ref([])
|
||||
const operateList = ref([
|
||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['collect:CommemorativeBanknote:query'] },
|
||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['collect:CommemorativeBanknote:edit'] },
|
||||
{ id: 'copy', icon: 'DocumentCopy', title: '复制', hasPermi: ['collect:CommemorativeBanknote:add'] },
|
||||
{ id: 'delete', icon: 'Delete', title: '删除', hasPermi: ['collect:CommemorativeBanknote:remove'] }
|
||||
])
|
||||
const data = reactive({
|
||||
@@ -232,6 +233,9 @@ const handleOperate = (operate, row) => {
|
||||
case 'edit':
|
||||
handleUpdate(row)
|
||||
break
|
||||
case 'copy':
|
||||
handleCopy(row)
|
||||
break
|
||||
case 'delete':
|
||||
handleDelete(row)
|
||||
break
|
||||
@@ -365,6 +369,19 @@ function handleUpdate(row) {
|
||||
})
|
||||
}
|
||||
|
||||
/** 复制按钮操作 */
|
||||
function handleCopy(row) {
|
||||
reset()
|
||||
const _id = row.id || ids.value
|
||||
getCommemorativeBanknote(_id).then((response) => {
|
||||
form.value = response.data
|
||||
form.value.id = null // 清除ID,作为新增处理
|
||||
form.value.collectFileList = [] // 清除图片列表
|
||||
open.value = true
|
||||
title.value = '复制纪念钞'
|
||||
})
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs.CommemorativeBanknoteRef.validate((valid) => {
|
||||
|
||||
@@ -62,22 +62,23 @@
|
||||
<dict-tag :options="commemorative_coin_type" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="全称" align="center" prop="fullName" width="320" />
|
||||
<el-table-column label="简称" align="center" prop="shortName" width="320" />
|
||||
<el-table-column label="面值" align="center" prop="faceValue" />
|
||||
<el-table-column label="发行量" align="center" prop="mintage" />
|
||||
<el-table-column label="全称" align="center" prop="fullName" width="280" />
|
||||
<el-table-column label="简称" align="center" prop="shortName" width="280" />
|
||||
<el-table-column label="面值" align="center" prop="faceValue" width="120" />
|
||||
<el-table-column label="发行量" align="center" prop="mintage" width="120" />
|
||||
<el-table-column label="材质" align="center" prop="composition">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="commemorative_coin_composition" :value="scope.row.composition" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="发行时间" align="center" prop="issueDate" />
|
||||
<el-table-column label="发行时间" align="center" prop="issueDate" width="120" />
|
||||
<el-table-column label="排序" align="center" prop="setNumber" width="100" />
|
||||
<el-table-column label="系列" align="center" prop="series">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="commemorative_coin_series" :value="scope.row.series" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<el-table-column label="操作" width="200" align="center" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<div class="ctrl-btn d-flex">
|
||||
<el-tooltip v-for="item in operateList" :key="item.id" class="item" effect="dark" :content="item.title" placement="top">
|
||||
@@ -232,6 +233,7 @@ const title = ref('')
|
||||
const operateList = ref([
|
||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['collect:commemorativeCoin:query'] },
|
||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['collect:commemorativeCoin:edit'] },
|
||||
{ id: 'copy', icon: 'DocumentCopy', title: '复制', hasPermi: ['collect:commemorativeCoin:add'] },
|
||||
{ id: 'delete', icon: 'Delete', title: '删除', hasPermi: ['collect:commemorativeCoin:remove'] }
|
||||
])
|
||||
const data = reactive({
|
||||
@@ -281,6 +283,9 @@ const handleOperate = (operate, row) => {
|
||||
case 'edit':
|
||||
handleUpdate(row)
|
||||
break
|
||||
case 'copy':
|
||||
handleCopy(row)
|
||||
break
|
||||
case 'delete':
|
||||
handleDelete(row)
|
||||
break
|
||||
@@ -413,6 +418,19 @@ function handleUpdate(row) {
|
||||
})
|
||||
}
|
||||
|
||||
/** 复制按钮操作 */
|
||||
function handleCopy(row) {
|
||||
reset()
|
||||
const _id = row.id || ids.value
|
||||
getCommemorativeCoin(_id).then((response) => {
|
||||
form.value = response.data
|
||||
form.value.id = null // 清除ID,作为新增处理
|
||||
form.value.collectFileList = [] // 清除图片列表
|
||||
open.value = true
|
||||
title.value = '复制流通纪念币'
|
||||
})
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs.commemorativeCoinRef.validate((valid) => {
|
||||
|
||||
@@ -12,16 +12,16 @@
|
||||
<el-form-item label="发行年份" prop="issueYear">
|
||||
<el-date-picker v-model="queryParams.issueYear" type="year" placeholder="请选择发行年份" value-format="YYYY" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="系列" prop="series">
|
||||
<el-select v-model="queryParams.series" placeholder="请选择系列" clearable>
|
||||
<el-option v-for="dict in precious_metal_coin_series" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目题材" prop="blockType">
|
||||
<el-select v-model="queryParams.blockType" placeholder="请选择项目题材" clearable>
|
||||
<el-option v-for="dict in precious_metal_block_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="系列" prop="series">
|
||||
<el-select v-model="queryParams.series" placeholder="请选择系列" clearable>
|
||||
<el-option v-for="dict in precious_metal_coin_series" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="search-btn-con">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
@@ -53,17 +53,17 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="全称" align="center" prop="fullName" width="320" />
|
||||
<el-table-column label="简称" align="center" prop="shortName" width="320" />
|
||||
<el-table-column label="面额" align="center" prop="faceValue" />
|
||||
<el-table-column label="发行量" align="center" prop="mintage" />
|
||||
<el-table-column label="发行时间" align="center" prop="issueDate" />
|
||||
<el-table-column label="简称" align="center" prop="shortName" width="200" />
|
||||
<el-table-column label="面额" align="center" prop="faceValue" width="120" />
|
||||
<el-table-column label="发行量" align="center" prop="mintage" width="120" />
|
||||
<el-table-column label="发行时间" align="center" prop="issueDate" width="120" />
|
||||
<el-table-column label="系列" align="center" prop="series">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="precious_metal_coin_series" :value="scope.row.series" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="铸币单位" align="center" prop="coinageUnit" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<el-table-column label="操作" width="220" align="center" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<div class="ctrl-btn d-flex">
|
||||
<el-tooltip v-for="item in operateList" :key="item.id" class="item" effect="dark" :content="item.title" placement="top">
|
||||
@@ -223,6 +223,7 @@ const previewImageList = ref([])
|
||||
const operateList = ref([
|
||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['collect:preciousMetalCoin:query'] },
|
||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['collect:preciousMetalCoin:edit'] },
|
||||
{ id: 'copy', icon: 'DocumentCopy', title: '复制', hasPermi: ['collect:preciousMetalCoin:add'] },
|
||||
{ id: 'delete', icon: 'Delete', title: '删除', hasPermi: ['collect:preciousMetalCoin:remove'] }
|
||||
])
|
||||
const data = reactive({
|
||||
@@ -261,6 +262,9 @@ const handleOperate = (operate, row) => {
|
||||
case 'edit':
|
||||
handleUpdate(row)
|
||||
break
|
||||
case 'copy':
|
||||
handleCopy(row)
|
||||
break
|
||||
case 'delete':
|
||||
handleDelete(row)
|
||||
break
|
||||
@@ -404,6 +408,19 @@ function handleUpdate(row) {
|
||||
})
|
||||
}
|
||||
|
||||
/** 复制按钮操作 */
|
||||
function handleCopy(row) {
|
||||
reset()
|
||||
const _id = row.id || ids.value
|
||||
getPreciousMetalCoin(_id).then((response) => {
|
||||
form.value = response.data
|
||||
form.value.id = null // 清除ID,作为新增处理
|
||||
form.value.collectFileList = [] // 清除图片列表
|
||||
open.value = true
|
||||
title.value = '复制贵金属纪念币'
|
||||
})
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs.preciousMetalCoinRef.validate((valid) => {
|
||||
|
||||
@@ -50,7 +50,29 @@
|
||||
<el-table-column label="活动时长" align="center" prop="exerciseTimeStr" width="150"> </el-table-column>
|
||||
<el-table-column label="成员" align="center" prop="partner" />
|
||||
<el-table-column label="总费用(元)" width="120" align="center" prop="totalCost" />
|
||||
<el-table-column label="操作" width="140" align="center" class-name="small-padding fixed-width">
|
||||
<el-table-column label="附件" align="center" prop="attachment" width="120">
|
||||
<template #default="scope">
|
||||
<el-popover v-if="parseAttachments(scope.row.attachment).length" placement="left" width="280" trigger="click">
|
||||
<template #reference>
|
||||
<el-button link type="primary">查看{{ parseAttachments(scope.row.attachment).length }}个</el-button>
|
||||
</template>
|
||||
<div class="attachment-popover-list">
|
||||
<el-link
|
||||
v-for="(file, index) in parseAttachments(scope.row.attachment)"
|
||||
:key="file"
|
||||
type="primary"
|
||||
:href="getAttachmentUrl(file)"
|
||||
target="_blank"
|
||||
:underline="false"
|
||||
>
|
||||
{{ getAttachmentName(file, index) }}
|
||||
</el-link>
|
||||
</div>
|
||||
</el-popover>
|
||||
<span v-else class="attachment-empty">无</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<div class="ctrl-btn d-flex">
|
||||
<el-tooltip v-for="item in operateList" :key="item.id" class="item" effect="dark" :content="item.title" placement="top">
|
||||
@@ -115,6 +137,22 @@
|
||||
<el-form-item label="收获" style="width: 792px" prop="harvest">
|
||||
<el-input v-model="form.harvest" type="textarea" placeholder="请输入收获" />
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" style="width: 792px" prop="attachment">
|
||||
<div v-if="title === '查看活动记录'" class="attachment-readonly-list attachment-form-list">
|
||||
<el-link
|
||||
v-for="(file, index) in parseAttachments(form.attachment)"
|
||||
:key="file"
|
||||
type="primary"
|
||||
:href="getAttachmentUrl(file)"
|
||||
target="_blank"
|
||||
:underline="false"
|
||||
>
|
||||
{{ getAttachmentName(file, index) }}
|
||||
</el-link>
|
||||
<span v-if="!parseAttachments(form.attachment).length" class="attachment-empty">暂无附件</span>
|
||||
</div>
|
||||
<file-upload v-else v-model="form.attachment" :limit="9" :file-size="10" :file-type="attachmentFileTypes" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" style="width: 792px" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
@@ -126,16 +164,54 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 费用汇总对话框 -->
|
||||
<el-dialog title="费用汇总" v-model="summaryDialogVisible" width="1200px" append-to-body>
|
||||
<div v-loading="summaryLoading">
|
||||
<el-table :data="expenseList" @selection-change="handleExpenseSelectionChange" max-height="500">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="账户名称" align="center" width="180" prop="accountName" />
|
||||
<el-table-column label="交易时间" align="center" width="160" prop="createTime" />
|
||||
<el-table-column label="交易金额" align="center" width="100" prop="amount" />
|
||||
<el-table-column label="交易类别" align="center" width="140" prop="dealCategory">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="deal_category" :value="scope.row.dealCategory" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易子类别" align="center" width="140" prop="childCategoryName" />
|
||||
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<div style="margin-top: 16px; text-align: right; color: #606266; font-size: 14px">
|
||||
已选 <span style="color: #409eff; font-weight: bold">{{ selectedExpenses.length }}</span> 条, 合计金额:<span
|
||||
style="color: #f56c6c; font-weight: bold; font-size: 16px"
|
||||
>{{ calculateTotalAmount() }}</span
|
||||
>
|
||||
元
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="confirmSummary" :disabled="selectedExpenses.length === 0">确认汇总</el-button>
|
||||
<el-button @click="summaryDialogVisible = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Activity">
|
||||
import { listActivity, getActivity, delActivity, addActivity, updateActivity } from '@/api/health/activity'
|
||||
import { listAccountDealRecord } from '@/api/invest/accountDealRecord'
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import dayjs from 'dayjs'
|
||||
import { require } from '@/utils/require'
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { activity_type, activity_exercise } = proxy.useDict('activity_type', 'activity_exercise')
|
||||
const { activity_type, activity_exercise, deal_category, daily_expenses } = proxy.useDict(
|
||||
'activity_type',
|
||||
'activity_exercise',
|
||||
'deal_category',
|
||||
'daily_expenses'
|
||||
)
|
||||
|
||||
const activityList = ref([])
|
||||
const open = ref(false)
|
||||
@@ -146,9 +222,17 @@ const single = ref(true)
|
||||
const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const title = ref('')
|
||||
const summaryDialogVisible = ref(false)
|
||||
const summaryLoading = ref(false)
|
||||
const expenseList = ref([])
|
||||
const selectedExpenses = ref([])
|
||||
const currentActivityId = ref(null)
|
||||
const summaryTotal = ref(0)
|
||||
const attachmentFileTypes = ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx']
|
||||
const operateList = ref([
|
||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['health:activity:query'] },
|
||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['health:activity:edit'] },
|
||||
{ id: 'summary', icon: 'Money', title: '费用汇总', hasPermi: ['health:activity:edit'] },
|
||||
{ id: 'delete', icon: 'Delete', title: '删除', hasPermi: ['health:activity:remove'] }
|
||||
])
|
||||
const data = reactive({
|
||||
@@ -182,6 +266,9 @@ const handleOperate = (operate, row) => {
|
||||
case 'edit':
|
||||
handleUpdate(row)
|
||||
break
|
||||
case 'summary':
|
||||
handleCostSummary(row)
|
||||
break
|
||||
case 'delete':
|
||||
handleDelete(row)
|
||||
break
|
||||
@@ -192,6 +279,39 @@ const handleOperate = (operate, row) => {
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data)
|
||||
|
||||
function parseAttachments(attachment) {
|
||||
if (!attachment) {
|
||||
return []
|
||||
}
|
||||
return String(attachment)
|
||||
.split(',')
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
function getAttachmentUrl(file) {
|
||||
if (!file) {
|
||||
return ''
|
||||
}
|
||||
if (/^(https?:)?\/\//.test(file) || file.startsWith('/') || file.startsWith('blob:') || file.startsWith('data:')) {
|
||||
return file
|
||||
}
|
||||
return `/${file}`
|
||||
}
|
||||
|
||||
function getAttachmentName(file, index) {
|
||||
const purePath = String(file || '').split('?')[0]
|
||||
const name = purePath.slice(purePath.lastIndexOf('/') + 1)
|
||||
if (!name) {
|
||||
return `附件${index + 1}`
|
||||
}
|
||||
try {
|
||||
return decodeURIComponent(name)
|
||||
} catch {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询活动记录列表 */
|
||||
function getList() {
|
||||
loading.value = true
|
||||
@@ -229,7 +349,8 @@ function reset() {
|
||||
remark: null,
|
||||
totalCost: 0,
|
||||
partner: null,
|
||||
costDetail: null
|
||||
costDetail: null,
|
||||
attachment: null
|
||||
}
|
||||
proxy.resetForm('activityRef')
|
||||
}
|
||||
@@ -345,5 +466,131 @@ function handleExport() {
|
||||
)
|
||||
}
|
||||
|
||||
// 费用汇总按钮操作
|
||||
function handleCostSummary(row) {
|
||||
currentActivityId.value = row.id
|
||||
summaryDialogVisible.value = true
|
||||
summaryLoading.value = true
|
||||
|
||||
// 构建查询参数
|
||||
const queryExpenseParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 1000,
|
||||
dealCategory: '1', // 日常支出
|
||||
startTime: row.startTime ? dayjs(row.startTime).format('YYYY-MM-DD') : '',
|
||||
endTime: row.endTime ? dayjs(row.endTime).format('YYYY-MM-DD') : dayjs().format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
// 查询日常支出列表
|
||||
listAccountDealRecord(queryExpenseParams)
|
||||
.then((response) => {
|
||||
expenseList.value = response.rows || []
|
||||
summaryLoading.value = false
|
||||
})
|
||||
.catch(() => {
|
||||
summaryLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 多选框选中数据(费用汇总)
|
||||
function handleExpenseSelectionChange(selection) {
|
||||
selectedExpenses.value = selection
|
||||
}
|
||||
|
||||
// 计算选中费用总额
|
||||
function calculateTotalAmount() {
|
||||
return selectedExpenses.value
|
||||
.reduce((sum, item) => {
|
||||
return sum + (parseFloat(item.amount) || 0)
|
||||
}, 0)
|
||||
.toFixed(2)
|
||||
}
|
||||
|
||||
// 确认汇总
|
||||
function confirmSummary() {
|
||||
if (selectedExpenses.value.length === 0) {
|
||||
proxy.$modal.msgWarning('请至少选择一条费用记录')
|
||||
return
|
||||
}
|
||||
|
||||
// 获取活动详情
|
||||
getActivity(currentActivityId.value).then((response) => {
|
||||
const activity = response.data
|
||||
|
||||
// 计算总金额
|
||||
const totalAmount = parseFloat(calculateTotalAmount())
|
||||
const currentTotal = parseFloat(activity.totalCost) || 0
|
||||
|
||||
// 判断活动是否跨天
|
||||
const startDate = activity.startTime ? dayjs(activity.startTime).format('YYYY-MM-DD') : ''
|
||||
const endDate = activity.endTime ? dayjs(activity.endTime).format('YYYY-MM-DD') : ''
|
||||
const isMultiDay = startDate !== endDate
|
||||
|
||||
// 构建费用明细
|
||||
let detailsWithDate = ''
|
||||
|
||||
if (isMultiDay) {
|
||||
// 多天活动:按日期分组
|
||||
const groupedByDate = {}
|
||||
selectedExpenses.value.forEach((item) => {
|
||||
const date = dayjs(item.createTime).format('MM-DD')
|
||||
if (!groupedByDate[date]) {
|
||||
groupedByDate[date] = []
|
||||
}
|
||||
const category = item.childCategoryName || ''
|
||||
groupedByDate[date].push(`${category} ${item.amount}元${item.remark ? '(' + item.remark + ')' : ''}`)
|
||||
})
|
||||
|
||||
// 按日期组装明细
|
||||
detailsWithDate = Object.keys(groupedByDate)
|
||||
.sort()
|
||||
.map((date) => `${date} ${groupedByDate[date].join('、')}`)
|
||||
.join(';')
|
||||
} else {
|
||||
// 单天活动:不带日期
|
||||
detailsWithDate = selectedExpenses.value
|
||||
.map((item) => {
|
||||
const category = item.childCategoryName || ''
|
||||
return `${category} ${item.amount}元${item.remark ? '(' + item.remark + ')' : ''}`
|
||||
})
|
||||
.join(';')
|
||||
}
|
||||
|
||||
// 更新活动费用
|
||||
const updateData = {
|
||||
...activity,
|
||||
totalCost: (currentTotal + totalAmount).toFixed(2),
|
||||
costDetail: activity.costDetail ? `${activity.costDetail};${detailsWithDate}` : detailsWithDate
|
||||
}
|
||||
|
||||
updateActivity(updateData).then(() => {
|
||||
proxy.$modal.msgSuccess('费用汇总成功')
|
||||
summaryDialogVisible.value = false
|
||||
selectedExpenses.value = []
|
||||
getList()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.attachment-popover-list,
|
||||
.attachment-readonly-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.attachment-form-list {
|
||||
min-height: 32px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.attachment-empty {
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
|
||||
582
src/views/health/activityStatistic/index.vue
Normal file
582
src/views/health/activityStatistic/index.vue
Normal file
@@ -0,0 +1,582 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="search-con">
|
||||
<div class="title">查询条件</div>
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" :rules="rules" label-width="100px">
|
||||
<el-form-item label="活动名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入活动名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动类型" prop="activityType">
|
||||
<el-select v-model="queryParams.activityType" placeholder="请选择活动类型" clearable>
|
||||
<el-option v-for="dict in activity_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动量" prop="activityVolume">
|
||||
<el-select v-model="queryParams.activityVolume" placeholder="请选择活动量" clearable>
|
||||
<el-option v-for="dict in activity_exercise" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动地点" prop="place">
|
||||
<el-input v-model="queryParams.place" placeholder="请输入活动地点" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="统计粒度" prop="type">
|
||||
<el-radio-group v-model="queryParams.type" @change="handleTimeChange">
|
||||
<el-radio-button :label="1">日</el-radio-button>
|
||||
<el-radio-button :label="2">月</el-radio-button>
|
||||
<el-radio-button :label="3">年</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动日期" prop="time" v-if="queryParams.type === 1">
|
||||
<el-date-picker
|
||||
v-model="queryParams.time"
|
||||
type="daterange"
|
||||
range-separator="~"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
format="YYYY-MM-DD"
|
||||
@calendar-change="calendarChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动月份" prop="time" v-if="queryParams.type === 2">
|
||||
<el-date-picker
|
||||
v-model="queryParams.time"
|
||||
type="monthrange"
|
||||
range-separator="~"
|
||||
format="YYYY-MM"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
:disabled-date="disabledDateFun"
|
||||
@calendar-change="calendarChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动年份" prop="time" v-if="queryParams.type === 3">
|
||||
<yearPicker
|
||||
v-model="queryParams.time"
|
||||
ref="statisticPicker"
|
||||
labelText="选择年份"
|
||||
:initYear="dateValue"
|
||||
:showYear="showYearValue"
|
||||
:maxLength="5"
|
||||
sp="~"
|
||||
@updateTimeRange="updateStatisticYear"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="search-btn-con">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main-con activity-stat-main" style="height: calc(100% - 1.45rem)">
|
||||
<div class="summary-con" style="height: 115px">
|
||||
<div class="right-con">
|
||||
<div class="icon-box">
|
||||
<el-icon :size="40" color="#409EFF"><Histogram /></el-icon>
|
||||
</div>
|
||||
<div class="item-wrap">
|
||||
<div class="title">活动次数</div>
|
||||
<div>
|
||||
<span class="num">{{ activity.activityCount || 0 }}</span
|
||||
><span class="unit">次</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-con">
|
||||
<div class="icon-box">
|
||||
<el-icon :size="40" color="#67C23A"><Calendar /></el-icon>
|
||||
</div>
|
||||
<div class="item-wrap">
|
||||
<div class="title">活动天数</div>
|
||||
<div>
|
||||
<span class="num">{{ activity.activityDays || 0 }}</span
|
||||
><span class="unit">天</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-con">
|
||||
<div class="icon-box">
|
||||
<el-icon :size="40" color="#E6A23C"><Timer /></el-icon>
|
||||
</div>
|
||||
<div class="item-wrap">
|
||||
<div class="title">总时长</div>
|
||||
<div>
|
||||
<span class="num">{{ activity.totalHours || 0 }}</span
|
||||
><span class="unit">小时</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-con">
|
||||
<div class="icon-box">
|
||||
<el-icon :size="40" color="#F56C6C"><Money /></el-icon>
|
||||
</div>
|
||||
<div class="item-wrap">
|
||||
<div class="title">活动费用</div>
|
||||
<div>
|
||||
<span class="num">{{ activity.totalCost || 0 }}</span
|
||||
><span class="unit">元</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-con">
|
||||
<div class="icon-box">
|
||||
<el-icon :size="40" color="#67C23A"><DataLine /></el-icon>
|
||||
</div>
|
||||
<div class="item-wrap">
|
||||
<div class="title">平均时长</div>
|
||||
<div>
|
||||
<span class="num">{{ activity.avgHours || 0 }}</span
|
||||
><span class="unit">小时/次</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-con" style="height: 115px">
|
||||
<div class="right-con">
|
||||
<div class="icon-box">
|
||||
<el-icon :size="40" color="#E6A23C"><Coin /></el-icon>
|
||||
</div>
|
||||
<div class="item-wrap">
|
||||
<div class="title">平均费用</div>
|
||||
<div>
|
||||
<span class="num">{{ activity.avgCost || 0 }}</span
|
||||
><span class="unit">元/次</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-con">
|
||||
<div class="icon-box">
|
||||
<el-icon :size="40" color="#F56C6C"><Medal /></el-icon>
|
||||
</div>
|
||||
<div class="item-wrap highlight-item">
|
||||
<div class="title">最高费用活动</div>
|
||||
<div class="summary-detail">
|
||||
<span class="num text-name">{{ activity.maxCostName || '--' }}</span>
|
||||
<span class="unit">{{ activity.maxCost || 0 }}元</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-con">
|
||||
<div class="icon-box">
|
||||
<el-icon :size="40" color="#409EFF"><Stopwatch /></el-icon>
|
||||
</div>
|
||||
<div class="item-wrap highlight-item">
|
||||
<div class="title">最长活动</div>
|
||||
<div class="summary-detail">
|
||||
<span class="num text-name">{{ activity.longestName || '--' }}</span>
|
||||
<span class="unit">{{ activity.longestHours || 0 }}小时</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-con">
|
||||
<div class="icon-box">
|
||||
<el-icon :size="40" color="#67C23A"><Location /></el-icon>
|
||||
</div>
|
||||
<div class="item-wrap highlight-item">
|
||||
<div class="title">常去地点</div>
|
||||
<div class="summary-detail">
|
||||
<span class="num text-name">{{ topPlace.name }}</span>
|
||||
<span class="unit">{{ topPlace.count }}次</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-con">
|
||||
<div class="icon-box">
|
||||
<el-icon :size="40" color="#409EFF"><User /></el-icon>
|
||||
</div>
|
||||
<div class="item-wrap highlight-item">
|
||||
<div class="title">常参与成员</div>
|
||||
<div class="summary-detail">
|
||||
<span class="num text-name">{{ topPartner.name }}</span>
|
||||
<span class="unit">{{ topPartner.count }}次</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="title-con" style="margin-top: 18px">
|
||||
<div class="title">活动统计</div>
|
||||
<div class="operate-btn-con">
|
||||
<el-radio-group v-model="radioVal" @change="handleRadioChange">
|
||||
<el-radio-button label="活动明细" />
|
||||
<el-radio-button label="次数趋势" />
|
||||
<el-radio-button label="费用趋势" />
|
||||
<el-radio-button label="时长趋势" />
|
||||
<el-radio-button label="类型分布" />
|
||||
<el-radio-button label="地点排行" />
|
||||
<el-radio-button label="成员排行" />
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-con">
|
||||
<div v-show="radioVal === '次数趋势'" class="chart" id="activityCountChart" style="height: calc(100% - 245px); margin-top: -10px"></div>
|
||||
<div v-show="radioVal === '费用趋势'" class="chart" id="activityCostChart" style="height: calc(100% - 245px); margin-top: -10px"></div>
|
||||
<div v-show="radioVal === '时长趋势'" class="chart" id="activityDurationChart" style="height: calc(100% - 245px); margin-top: -10px"></div>
|
||||
<div v-show="radioVal === '类型分布'" class="chart-grid" style="height: calc(100% - 245px)">
|
||||
<div class="chart" id="activityTypeChart"></div>
|
||||
<div class="chart" id="activityVolumeChart"></div>
|
||||
</div>
|
||||
<el-table v-show="radioVal === '地点排行'" v-loading="loading" :data="activity.placeList || []" height="calc(100% - 245px)" class="wrap-table">
|
||||
<el-table-column label="序号" width="70" type="index" align="center" />
|
||||
<el-table-column label="活动地点" align="center" prop="name" min-width="220" class-name="wrap-cell" />
|
||||
<el-table-column label="活动次数" align="center" prop="count" />
|
||||
<el-table-column label="累计时长(小时)" align="center" prop="hours" />
|
||||
<el-table-column label="累计费用(元)" align="center" prop="cost" />
|
||||
</el-table>
|
||||
<el-table v-show="radioVal === '成员排行'" v-loading="loading" :data="activity.partnerList || []" height="calc(100% - 245px)" class="wrap-table">
|
||||
<el-table-column label="序号" width="70" type="index" align="center" />
|
||||
<el-table-column label="成员" align="center" prop="name" min-width="220" class-name="wrap-cell" />
|
||||
<el-table-column label="参与次数" align="center" prop="count" />
|
||||
<el-table-column label="累计时长(小时)" align="center" prop="hours" />
|
||||
<el-table-column label="关联费用(元)" align="center" prop="cost" />
|
||||
</el-table>
|
||||
<el-table v-show="radioVal === '活动明细'" v-loading="loading" :data="activity.tableList || []" height="calc(100% - 245px)" class="wrap-table">
|
||||
<el-table-column label="序号" width="60" type="index" align="center" />
|
||||
<el-table-column label="活动名称" align="center" prop="name" min-width="180" class-name="wrap-cell" />
|
||||
<el-table-column label="活动类型" align="center" prop="typeName" width="120" />
|
||||
<el-table-column label="活动量" align="center" prop="activityVolumeName" width="110" />
|
||||
<el-table-column label="活动地点" align="center" prop="place" min-width="160" class-name="wrap-cell" />
|
||||
<el-table-column label="成员" align="center" prop="partner" min-width="180" class-name="wrap-cell" />
|
||||
<el-table-column label="开始时间" align="center" prop="startTime" width="170" />
|
||||
<el-table-column label="结束时间" align="center" prop="endTime" width="170" />
|
||||
<el-table-column label="活动时长" align="center" prop="duration" width="130" />
|
||||
<el-table-column label="费用(元)" align="center" prop="totalCost" width="110" />
|
||||
<el-table-column label="饮食" align="center" prop="foods" min-width="220" class-name="wrap-cell" />
|
||||
<el-table-column label="收获" align="center" prop="harvest" min-width="220" class-name="wrap-cell" />
|
||||
<el-table-column label="备注" align="center" prop="remark" min-width="220" class-name="wrap-cell" />
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ActivityStatistic">
|
||||
import dayjs from 'dayjs'
|
||||
import * as echarts from 'echarts'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Calendar, Coin, DataLine, Histogram, Location, Medal, Money, Stopwatch, Timer, User } from '@element-plus/icons-vue'
|
||||
import yearPicker from '@/components/YearPicker/index.vue'
|
||||
import { getActivityAnalysis } from '@/api/health/statisticAnalysis'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { activity_type, activity_exercise } = proxy.useDict('activity_type', 'activity_exercise')
|
||||
|
||||
const loading = ref(false)
|
||||
const radioVal = ref('活动明细')
|
||||
const currentType = ref('活动明细')
|
||||
const firstChooseDate = ref('')
|
||||
const dateValue = ref({ startYear: 2000, endYear: new Date().getFullYear() })
|
||||
const showYearValue = ref({ startShowYear: '', endShowYear: '' })
|
||||
const activity = ref({})
|
||||
const chartData = ref({
|
||||
name: [],
|
||||
count: [],
|
||||
cost: [],
|
||||
duration: []
|
||||
})
|
||||
|
||||
const dateValidate = (rules, value, callback) => {
|
||||
const dateType = rules.dateType || 'months'
|
||||
const num = rules.num || 23
|
||||
const message = rules.message || `时间跨度不能超过${num}个月`
|
||||
if (value && value.length === 2) {
|
||||
const start = value[0]
|
||||
const end = value[1]
|
||||
if (dayjs(end).diff(dayjs(start), dateType) > num) {
|
||||
queryParams.value.time = [start, dayjs(start).add(num, dateType)]
|
||||
ElMessage.warning(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const data = reactive({
|
||||
queryParams: {
|
||||
type: 1,
|
||||
time: null,
|
||||
name: null,
|
||||
activityType: null,
|
||||
activityVolume: null,
|
||||
place: null
|
||||
},
|
||||
rules: {
|
||||
time: [{ validator: dateValidate, dateType: 'months', num: 23, message: '时间跨度不能大于24个月,默认选择开始日期的24个月' }]
|
||||
}
|
||||
})
|
||||
|
||||
const { queryParams, rules } = toRefs(data)
|
||||
|
||||
const topPlace = computed(() => {
|
||||
const item = (activity.value.placeList || [])[0] || {}
|
||||
return { name: item.name || '--', count: item.count || 0 }
|
||||
})
|
||||
|
||||
const topPartner = computed(() => {
|
||||
const item = (activity.value.partnerList || [])[0] || {}
|
||||
return { name: item.name || '--', count: item.count || 0 }
|
||||
})
|
||||
|
||||
const updateStatisticYear = (startYear, endYear) => {
|
||||
queryParams.value.time = [new Date(startYear, 0, 1), new Date(endYear, 0, 1)]
|
||||
if (endYear - startYear > 5) {
|
||||
ElMessage.warning('时间跨度不能大于5年,默认选择开始日期的5年')
|
||||
queryParams.value.time = [new Date(startYear, 0, 1), new Date(startYear + 5, 0, 1)]
|
||||
}
|
||||
}
|
||||
|
||||
const disabledDateFun = (time) => {
|
||||
const arr = [0, 30, 365, 365 * 5]
|
||||
const days = arr[queryParams.value.type]
|
||||
if (firstChooseDate.value) {
|
||||
const one = days * 24 * 3600 * 1000
|
||||
const minTime = firstChooseDate.value - one
|
||||
const maxTime = firstChooseDate.value + one
|
||||
const endTime = maxTime > Date.now() ? Date.now() : maxTime
|
||||
return time.getTime() > endTime || time.getTime() < minTime
|
||||
}
|
||||
return time.getTime() > Date.now()
|
||||
}
|
||||
|
||||
const calendarChange = (val) => {
|
||||
firstChooseDate.value = val[0].getTime()
|
||||
if (val[1]) firstChooseDate.value = ''
|
||||
}
|
||||
|
||||
function handleTimeChange(type) {
|
||||
queryParams.value.time = null
|
||||
if (type === 1) {
|
||||
const end = dayjs().format('YYYY-MM-DD')
|
||||
queryParams.value.time = [dayjs(end).add(-60, 'day'), end]
|
||||
} else if (type === 2) {
|
||||
const end = dayjs().format('YYYY-MM')
|
||||
queryParams.value.time = [dayjs(end).add(-11, 'months'), end]
|
||||
} else if (type === 3) {
|
||||
const endYear = new Date().getFullYear()
|
||||
queryParams.value.time = [new Date(endYear - 2, 0, 1), new Date(endYear, 0, 1)]
|
||||
showYearValue.value = { startShowYear: endYear - 2, endShowYear: endYear }
|
||||
}
|
||||
getList()
|
||||
}
|
||||
|
||||
function getList() {
|
||||
loading.value = true
|
||||
chartData.value = { name: [], count: [], cost: [], duration: [] }
|
||||
const { type, time, name, activityType, activityVolume, place } = queryParams.value
|
||||
let formatValue = 'YYYY-MM-DD'
|
||||
if (type === 2) formatValue = 'YYYY-MM'
|
||||
if (type === 3) formatValue = 'YYYY'
|
||||
const params = {
|
||||
type,
|
||||
startTime: time && time.length > 0 ? dayjs(time[0]).format(formatValue) : '',
|
||||
endTime: time && time.length > 0 ? dayjs(time[1]).format(formatValue) : '',
|
||||
name,
|
||||
activityType,
|
||||
activityVolume,
|
||||
place
|
||||
}
|
||||
getActivityAnalysis(params)
|
||||
.then((response) => {
|
||||
activity.value = response.data || {}
|
||||
const countMap = new Map((activity.value.countTrend || []).map((item) => [item.time, Number(item.value || 0)]))
|
||||
const costMap = new Map((activity.value.costTrend || []).map((item) => [item.time, Number(item.value || 0)]))
|
||||
const durationMap = new Map((activity.value.durationTrend || []).map((item) => [item.time, Number(item.value || 0)]))
|
||||
const names = [
|
||||
...new Set(
|
||||
[...(activity.value.countTrend || []), ...(activity.value.costTrend || []), ...(activity.value.durationTrend || [])].map((item) => item.time)
|
||||
)
|
||||
].sort()
|
||||
chartData.value = {
|
||||
name: names,
|
||||
count: names.map((time) => countMap.get(time) || 0),
|
||||
cost: names.map((time) => costMap.get(time) || 0),
|
||||
duration: names.map((time) => durationMap.get(time) || 0)
|
||||
}
|
||||
handleRadioChange(currentType.value)
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function handleQuery() {
|
||||
getList()
|
||||
}
|
||||
|
||||
function resetQuery() {
|
||||
proxy.resetForm('queryRef')
|
||||
queryParams.value.name = null
|
||||
queryParams.value.activityType = null
|
||||
queryParams.value.activityVolume = null
|
||||
queryParams.value.place = null
|
||||
handleTimeChange(1)
|
||||
}
|
||||
|
||||
function chartBaseOption(data, seriesName, unit, color) {
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
formatter: (params) => `${params[0].name}<br/>${seriesName}:${params[0].value}${unit}`
|
||||
},
|
||||
grid: { left: '5%', right: '5%', bottom: '5%', containLabel: true },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: data.name,
|
||||
axisTick: { alignWithLabel: true },
|
||||
axisLine: { lineStyle: { width: 1, color: '#999' } }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisTick: { show: true },
|
||||
axisLine: { show: true, lineStyle: { width: 1, color: '#999' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: seriesName,
|
||||
data: data.value,
|
||||
type: data.type,
|
||||
barWidth: 12,
|
||||
smooth: true,
|
||||
lineStyle: { color },
|
||||
itemStyle: { color },
|
||||
areaStyle:
|
||||
data.type === 'line'
|
||||
? {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: `${color}99` },
|
||||
{ offset: 1, color: `${color}16` }
|
||||
])
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
function renderTrendChart(id, value, name, unit, color, type = 'bar') {
|
||||
if (document.getElementById(id) === null) return
|
||||
echarts.dispose(document.getElementById(id))
|
||||
const myChart = echarts.init(document.getElementById(id))
|
||||
myChart.setOption(chartBaseOption({ name: chartData.value.name, value, type }, name, unit, color))
|
||||
setTimeout(() => myChart.resize(), 100)
|
||||
}
|
||||
|
||||
function renderPieChart(id, data, title) {
|
||||
if (document.getElementById(id) === null) return
|
||||
echarts.dispose(document.getElementById(id))
|
||||
const myChart = echarts.init(document.getElementById(id))
|
||||
myChart.setOption({
|
||||
title: { text: title, left: 'center', top: 16 },
|
||||
tooltip: { trigger: 'item' },
|
||||
legend: { bottom: 10 },
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['42%', '70%'],
|
||||
center: ['50%', '48%'],
|
||||
data,
|
||||
label: { formatter: '{b}\n{c}次' }
|
||||
}
|
||||
]
|
||||
})
|
||||
setTimeout(() => myChart.resize(), 100)
|
||||
}
|
||||
|
||||
function handleRadioChange(type) {
|
||||
currentType.value = type
|
||||
nextTick(() => {
|
||||
switch (type) {
|
||||
case '次数趋势':
|
||||
renderTrendChart('activityCountChart', chartData.value.count, '活动次数', '次', '#409EFF', 'bar')
|
||||
break
|
||||
case '费用趋势':
|
||||
renderTrendChart('activityCostChart', chartData.value.cost, '活动费用', '元', '#E6A23C', 'line')
|
||||
break
|
||||
case '时长趋势':
|
||||
renderTrendChart('activityDurationChart', chartData.value.duration, '活动时长', '小时', '#67C23A', 'line')
|
||||
break
|
||||
case '类型分布':
|
||||
renderPieChart('activityTypeChart', activity.value.typeList || [], '活动类型分布')
|
||||
renderPieChart('activityVolumeChart', activity.value.volumeList || [], '活动量分布')
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
handleTimeChange(1)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.activity-stat-main {
|
||||
.summary-con {
|
||||
.right-con,
|
||||
.icon-box,
|
||||
.item-wrap,
|
||||
.num,
|
||||
.title {
|
||||
list-style: none !important;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
display: none !important;
|
||||
content: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.text-name {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
overflow: visible;
|
||||
font-size: 22px !important;
|
||||
line-height: 1.2;
|
||||
text-overflow: initial;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.highlight-item {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.summary-detail {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
|
||||
.unit {
|
||||
display: block;
|
||||
margin-left: 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
|
||||
.wrap-table {
|
||||
:deep(.el-table__cell) {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
:deep(.wrap-cell .cell) {
|
||||
overflow: visible;
|
||||
line-height: 20px;
|
||||
text-overflow: initial;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.chart-grid .chart {
|
||||
min-height: 320px;
|
||||
}
|
||||
</style>
|
||||
1010
src/views/health/chronicDisease/index.vue
Normal file
1010
src/views/health/chronicDisease/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -63,6 +63,28 @@
|
||||
<el-table-column label="就诊时间" align="center" prop="visitingTime" width="160"> </el-table-column>
|
||||
<el-table-column label="总费用(元)" align="center" width="120" prop="totalCost" />
|
||||
<el-table-column label="诊断结果" align="center" prop="diagnosis" />
|
||||
<el-table-column label="附件" align="center" prop="attachment" width="120">
|
||||
<template #default="scope">
|
||||
<el-popover v-if="parseAttachments(scope.row.attachment).length" placement="left" width="280" trigger="click">
|
||||
<template #reference>
|
||||
<el-button link type="primary">查看{{ parseAttachments(scope.row.attachment).length }}个</el-button>
|
||||
</template>
|
||||
<div class="attachment-popover-list">
|
||||
<el-link
|
||||
v-for="(file, index) in parseAttachments(scope.row.attachment)"
|
||||
:key="file"
|
||||
type="primary"
|
||||
:href="getAttachmentUrl(file)"
|
||||
target="_blank"
|
||||
:underline="false"
|
||||
>
|
||||
{{ getAttachmentName(file, index) }}
|
||||
</el-link>
|
||||
</div>
|
||||
</el-popover>
|
||||
<span v-else class="attachment-empty">无</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="180" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<div class="ctrl-btn d-flex">
|
||||
@@ -132,6 +154,22 @@
|
||||
<el-form-item label="费用明细" style="width: 792px" prop="costDetail">
|
||||
<el-input v-model="form.costDetail" type="textarea" placeholder="请输入费用明细" />
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" style="width: 792px" prop="attachment">
|
||||
<div v-if="title === '查看就医记录'" class="attachment-readonly-list attachment-form-list">
|
||||
<el-link
|
||||
v-for="(file, index) in parseAttachments(form.attachment)"
|
||||
:key="file"
|
||||
type="primary"
|
||||
:href="getAttachmentUrl(file)"
|
||||
target="_blank"
|
||||
:underline="false"
|
||||
>
|
||||
{{ getAttachmentName(file, index) }}
|
||||
</el-link>
|
||||
<span v-if="!parseAttachments(form.attachment).length" class="attachment-empty">暂无附件</span>
|
||||
</div>
|
||||
<file-upload v-else v-model="form.attachment" :limit="9" :file-size="10" :file-type="attachmentFileTypes" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" style="width: 792px" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
@@ -287,6 +325,7 @@ const total = ref(0)
|
||||
const title = ref('')
|
||||
const personList = ref([])
|
||||
const healthRecordList = ref([])
|
||||
const attachmentFileTypes = ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx']
|
||||
|
||||
const detailOpen = ref(false)
|
||||
const openDetail = ref(false)
|
||||
@@ -395,8 +434,41 @@ const handleOperate = (operate, row) => {
|
||||
|
||||
const { queryParams, form, formDetail, rules, queryPersonParams, queryMedicineParams, queryHealthRecordParams, rulesDetail } = toRefs(data)
|
||||
|
||||
function parseAttachments(attachment) {
|
||||
if (!attachment) {
|
||||
return []
|
||||
}
|
||||
return String(attachment)
|
||||
.split(',')
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
function getAttachmentUrl(file) {
|
||||
if (!file) {
|
||||
return ''
|
||||
}
|
||||
if (/^(https?:)?\/\//.test(file) || file.startsWith('/') || file.startsWith('blob:') || file.startsWith('data:')) {
|
||||
return file
|
||||
}
|
||||
return `/${file}`
|
||||
}
|
||||
|
||||
function getAttachmentName(file, index) {
|
||||
const purePath = String(file || '').split('?')[0]
|
||||
const name = purePath.slice(purePath.lastIndexOf('/') + 1)
|
||||
if (!name) {
|
||||
return `附件${index + 1}`
|
||||
}
|
||||
try {
|
||||
return decodeURIComponent(name)
|
||||
} catch {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
const handlePersonChange = (personId) => {
|
||||
queryHealthRecordParams.personId = personId
|
||||
queryHealthRecordParams.value.personId = personId
|
||||
listHealthRecord(queryHealthRecordParams).then((response) => {
|
||||
healthRecordList.value = response.rows
|
||||
if (response.rows.length > 0) {
|
||||
@@ -405,7 +477,7 @@ const handlePersonChange = (personId) => {
|
||||
})
|
||||
}
|
||||
const handleQueryPersonChange = (personId) => {
|
||||
queryHealthRecordParams.personId = personId
|
||||
queryHealthRecordParams.value.personId = personId
|
||||
queryParams.value.healthRecordId = null
|
||||
listHealthRecord(queryHealthRecordParams).then((response) => {
|
||||
healthRecordList.value = response.rows
|
||||
@@ -780,7 +852,8 @@ function reset() {
|
||||
personId: null,
|
||||
totalCost: 0,
|
||||
partner: null,
|
||||
costDetail: null
|
||||
costDetail: null,
|
||||
attachment: null
|
||||
}
|
||||
proxy.resetForm('doctorRecordRef')
|
||||
}
|
||||
@@ -825,7 +898,7 @@ function handleSelectionChange(selection) {
|
||||
// 查看
|
||||
const handleView = (row) => {
|
||||
title.value = '查看就医记录'
|
||||
queryHealthRecordParams.personId = row.personId
|
||||
queryHealthRecordParams.value.personId = row.personId
|
||||
listHealthRecord(queryHealthRecordParams).then((response) => {
|
||||
healthRecordList.value = response.rows
|
||||
})
|
||||
@@ -839,7 +912,7 @@ function handleAdd() {
|
||||
form.value.visitingTime = dayjs(new Date().getTime()).format('YYYY-MM-DD HH:mm:ss')
|
||||
if (personList.value.length > 0) {
|
||||
form.value.personId = personList.value[0].id
|
||||
queryHealthRecordParams.personId = personList.value[0].id
|
||||
queryHealthRecordParams.value.personId = personList.value[0].id
|
||||
listHealthRecord(queryHealthRecordParams).then((response) => {
|
||||
healthRecordList.value = response.rows
|
||||
if (response.rows.length > 0) {
|
||||
@@ -854,7 +927,7 @@ function handleAdd() {
|
||||
/** 修改按钮操作 */
|
||||
function handleUpdate(row) {
|
||||
reset()
|
||||
queryHealthRecordParams.personId = row.personId
|
||||
queryHealthRecordParams.value.personId = row.personId
|
||||
listHealthRecord(queryHealthRecordParams).then((response) => {
|
||||
healthRecordList.value = response.rows
|
||||
})
|
||||
@@ -916,3 +989,23 @@ function handleExport() {
|
||||
getList()
|
||||
getPersonList()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.attachment-popover-list,
|
||||
.attachment-readonly-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.attachment-form-list {
|
||||
min-height: 32px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.attachment-empty {
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
<div class="operate-btn-con">
|
||||
<el-button @click="handleAdd" icon="Plus" v-hasPermi="['health:healthRecord:add']">新增</el-button>
|
||||
<el-button :disabled="multiple" icon="Delete" @click="handleDelete" v-hasPermi="['health:healthRecord:remove']">删除</el-button>
|
||||
<el-button :disabled="single" icon="Download" @click="handleFullExport()" v-hasPermi="['health:healthRecord:export']">完整导出</el-button>
|
||||
<!-- <el-button @click="handleExport" icon="Download" v-hasPermi="['health:healthRecord:export']">导出</el-button> -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -323,6 +324,7 @@ const processRecordList = ref([])
|
||||
|
||||
const operateList = ref([
|
||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['health:healthRecord:query'] },
|
||||
{ id: 'fullExport', icon: 'Download', title: '完整导出', hasPermi: ['health:healthRecord:export'] },
|
||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['health:healthRecord:edit'] },
|
||||
{ id: 'delete', icon: 'Delete', title: '删除', hasPermi: ['health:healthRecord:remove'] }
|
||||
])
|
||||
@@ -381,6 +383,9 @@ const handleOperate = (operate, row) => {
|
||||
case 'edit':
|
||||
handleUpdate(row)
|
||||
break
|
||||
case 'fullExport':
|
||||
handleFullExport(row)
|
||||
break
|
||||
case 'delete':
|
||||
handleDelete(row)
|
||||
break
|
||||
@@ -623,6 +628,22 @@ function handleExport() {
|
||||
)
|
||||
}
|
||||
|
||||
function handleFullExport(row) {
|
||||
const recordId = row && row.id ? row.id : ids.value[0]
|
||||
const recordName = row && row.name ? row.name : '健康档案'
|
||||
if (!recordId) {
|
||||
proxy.$modal.msgWarning('请选择要导出的健康档案')
|
||||
return
|
||||
}
|
||||
proxy.download(
|
||||
'health/analysis/recordFullExport',
|
||||
{
|
||||
recordId
|
||||
},
|
||||
`${recordName}_完整信息_${new Date().getTime()}.xlsx`
|
||||
)
|
||||
}
|
||||
|
||||
getList()
|
||||
getPersonList()
|
||||
</script>
|
||||
|
||||
1864
src/views/health/healthScreen/index.vue
Normal file
1864
src/views/health/healthScreen/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
705
src/views/health/medicationPlan/index.vue
Normal file
705
src/views/health/medicationPlan/index.vue
Normal file
@@ -0,0 +1,705 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="search-con">
|
||||
<div class="title">查询条件</div>
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="计划名称" prop="planName">
|
||||
<el-input v-model="queryParams.planName" placeholder="请输入计划名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="人员" prop="personId">
|
||||
<el-select v-model="queryParams.personId" placeholder="请选择人员" clearable filterable>
|
||||
<el-option v-for="item in personList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="频率类型" prop="frequencyType">
|
||||
<el-select v-model="queryParams.frequencyType" placeholder="请选择频率类型" clearable>
|
||||
<el-option v-for="dict in medication_frequency_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
|
||||
<el-option v-for="dict in medication_plan_status" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="关键字" prop="keys">
|
||||
<el-input v-model="queryParams.keys" placeholder="计划名称/药品名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="search-btn-con">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main-con">
|
||||
<div class="title-con">
|
||||
<div class="title">用药计划</div>
|
||||
<div class="operate-btn-con">
|
||||
<el-button @click="handleAdd" icon="Plus" v-hasPermi="['health:medicationPlan:add']">新增</el-button>
|
||||
<el-button :disabled="multiple" icon="Delete" @click="handleDelete" v-hasPermi="['health:medicationPlan:remove']">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-con" v-loading="loading" height="calc(100% - 0.65rem)">
|
||||
<el-table v-loading="loading" :data="planList" @selection-change="handleSelectionChange" height="calc(100% - 0.65rem)">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column type="index" label="序号" :index="indexMethod" width="50"></el-table-column>
|
||||
<el-table-column label="计划名称" align="center" prop="planName" width="150" />
|
||||
<el-table-column label="人员" align="center" prop="personName" width="100" />
|
||||
<el-table-column label="药品" align="center" prop="medicineName" width="200" />
|
||||
<el-table-column label="剂量" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<span style="display: inline-flex; align-items: center; gap: 4px">
|
||||
{{ scope.row.dosagePerTime }}
|
||||
<dict-tag :options="medical_unit" :value="scope.row.dosageUnit" style="display: inline-flex" />
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="频次类型" align="center" prop="frequencyType" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="medication_frequency_type" :value="scope.row.frequencyType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="服药时间" align="center" width="180">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.timeSlots" class="time-slots-list">
|
||||
<el-tag v-for="(time, idx) in parseTimeSlots(scope.row.timeSlots)" :key="idx" size="small" type="info" class="time-tag">
|
||||
{{ time }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<span v-else>--</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="开始日期" align="center" prop="startDate" width="110">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.startDate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束日期" align="center" prop="endDate" width="110">
|
||||
<template #default="scope">
|
||||
{{ scope.row.endDate ? formatDate(scope.row.endDate) : '长期' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="今日进度" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.status === 1"> {{ scope.row.todayCompleted || 0 }}/{{ scope.row.todayTotal || 0 }} </span>
|
||||
<span v-else>--</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提醒" align="center" prop="remindEnabled" width="80">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.remindEnabled === 1" type="success">开</el-tag>
|
||||
<el-tag v-else type="info">关</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status" width="90">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="medication_plan_status" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding">
|
||||
<template v-slot="scope">
|
||||
<div class="ctrl-btn d-flex">
|
||||
<el-tooltip class="item" effect="dark" content="查看" placement="top">
|
||||
<el-button icon="View" v-hasPermi="['health:medicationPlan:query']" circle @click="handleView(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.status === 1" class="item" effect="dark" content="暂停" placement="top">
|
||||
<el-button icon="VideoPause" v-hasPermi="['health:medicationPlan:edit']" circle @click="handlePause(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.status === 2" class="item" effect="dark" content="恢复" placement="top">
|
||||
<el-button icon="VideoPlay" v-hasPermi="['health:medicationPlan:edit']" circle @click="handleResume(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.status !== 3" class="item" effect="dark" content="结束" placement="top">
|
||||
<el-button icon="Finished" v-hasPermi="['health:medicationPlan:edit']" circle @click="handleEnd(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" effect="dark" content="修改" placement="top">
|
||||
<el-button icon="Edit" v-hasPermi="['health:medicationPlan:edit']" circle @click="handleUpdate(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" effect="dark" content="删除" placement="top">
|
||||
<el-button icon="Delete" v-hasPermi="['health:medicationPlan:remove']" circle @click="handleDelete(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
small
|
||||
background
|
||||
layout="total,sizes, prev, pager, next"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 添加或修改用药计划对话框 -->
|
||||
<el-dialog :title="title" v-model="open" width="900px" append-to-body>
|
||||
<el-form ref="planRef" :model="form" :inline="true" :rules="rules" label-width="120px">
|
||||
<el-form-item label="计划名称" prop="planName">
|
||||
<el-input v-model="form.planName" placeholder="请输入计划名称(可选)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="人员" prop="personId">
|
||||
<el-select v-model="form.personId" placeholder="请选择人员" filterable @change="handlePersonChange">
|
||||
<el-option v-for="item in personList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="慢性疾病" prop="chronicDiseaseId">
|
||||
<el-select v-model="form.chronicDiseaseId" placeholder="请选择慢性疾病(可选)" filterable clearable>
|
||||
<el-option v-for="item in chronicDiseaseList" :key="item.id" :label="item.diseaseName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="药品" prop="medicineId">
|
||||
<el-select v-model="form.medicineId" placeholder="请选择药品" filterable @change="handleMedicineChange">
|
||||
<el-option v-for="item in medicineList" :key="item.id" :label="item.shortName + '-' + item.brand" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="每次剂量" prop="dosagePerTime">
|
||||
<el-input-number v-model="form.dosagePerTime" :min="0" :precision="2" style="width: 150px" />
|
||||
<el-select v-model="form.dosageUnit" placeholder="单位" style="width: 100px; margin-left: 10px">
|
||||
<el-option v-for="dict in medical_unit" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="频次类型" prop="frequencyType">
|
||||
<el-select v-model="form.frequencyType" placeholder="请选择频次类型">
|
||||
<el-option label="每天" :value="1" />
|
||||
<el-option label="每周" :value="2" />
|
||||
<el-option label="隔天" :value="3" />
|
||||
<el-option label="自定义" :value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="服药时间点" prop="timeSlots" style="width: 100%">
|
||||
<div class="time-slots-wrapper">
|
||||
<div v-for="(time, index) in form.timePointList" :key="index" class="time-slot-item">
|
||||
<span class="time-text">{{ time }}</span>
|
||||
<el-icon class="close-icon" @click="removeTimePoint(index)"><Close /></el-icon>
|
||||
</div>
|
||||
<el-time-picker
|
||||
v-model="newTimePoint"
|
||||
placeholder="添加时间"
|
||||
format="HH:mm"
|
||||
value-format="HH:mm"
|
||||
@change="addTimePoint"
|
||||
style="width: 110px"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期" prop="startDate">
|
||||
<el-date-picker v-model="form.startDate" type="date" placeholder="选择开始日期" value-format="YYYY-MM-DD" />
|
||||
</el-form-item>
|
||||
<el-form-item label="结束日期" prop="endDate">
|
||||
<el-date-picker v-model="form.endDate" type="date" placeholder="选择结束日期(可选)" value-format="YYYY-MM-DD" />
|
||||
</el-form-item>
|
||||
<el-form-item label="启用提醒" prop="remindEnabled">
|
||||
<el-switch v-model="form.remindEnabled" :active-value="1" :inactive-value="0" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.remindEnabled === 1" label="提前提醒" prop="remindAdvanceMin">
|
||||
<el-input-number v-model="form.remindAdvanceMin" :min="0" :max="60" />
|
||||
<span style="margin-left: 10px">分钟</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="通知家属" prop="notifyFamily">
|
||||
<el-switch v-model="form.notifyFamily" :active-value="1" :inactive-value="0" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.notifyFamily === 1" label="家属成员" prop="familyMemberIds">
|
||||
<el-select v-model="form.familyMemberIdList" multiple placeholder="请选择家属成员" filterable style="width: 300px">
|
||||
<el-option v-for="item in personList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" style="width: 100%">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" :rows="3" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template v-if="title !== '查看用药计划'" #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="MedicationPlan">
|
||||
import {
|
||||
listMedicationPlan,
|
||||
getMedicationPlan,
|
||||
delMedicationPlan,
|
||||
addMedicationPlan,
|
||||
updateMedicationPlan,
|
||||
pauseMedicationPlan,
|
||||
resumeMedicationPlan,
|
||||
endMedicationPlan
|
||||
} from '@/api/health/medicationPlan'
|
||||
import { listMedicineBasic } from '@/api/health/medicineBasic'
|
||||
import { listPerson } from '@/api/health/person'
|
||||
import { listChronicDiseaseByMember } from '@/api/health/chronicDisease'
|
||||
import { Close } from '@element-plus/icons-vue'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const route = useRoute()
|
||||
const { medication_frequency_type, medication_plan_status, medical_unit } = proxy.useDict('medication_frequency_type', 'medication_plan_status', 'medical_unit')
|
||||
|
||||
const planList = ref([])
|
||||
const personList = ref([])
|
||||
const medicineList = ref([])
|
||||
const chronicDiseaseList = ref([])
|
||||
const open = ref(false)
|
||||
const loading = ref(true)
|
||||
const showSearch = ref(true)
|
||||
const ids = ref([])
|
||||
const single = ref(true)
|
||||
const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const title = ref('')
|
||||
const newTimePoint = ref(null)
|
||||
|
||||
const data = reactive({
|
||||
form: {},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
planName: null,
|
||||
personId: null,
|
||||
chronicDiseaseId: null,
|
||||
frequencyType: null,
|
||||
status: null,
|
||||
keys: null,
|
||||
keyword: null
|
||||
},
|
||||
rules: {
|
||||
personId: [{ required: true, message: '请选择人员', trigger: 'change' }],
|
||||
medicineId: [{ required: true, message: '请选择药品', trigger: 'change' }],
|
||||
dosagePerTime: [{ required: true, message: '请输入剂量', trigger: 'blur' }],
|
||||
startDate: [{ required: true, message: '请选择开始日期', trigger: 'change' }]
|
||||
}
|
||||
})
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data)
|
||||
|
||||
/** 查询用药计划列表 */
|
||||
function getList() {
|
||||
loading.value = true
|
||||
const params = { ...queryParams.value, keyword: queryParams.value.keys }
|
||||
listMedicationPlan(params).then((response) => {
|
||||
planList.value = response.rows
|
||||
total.value = response.total
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询人员列表 */
|
||||
function getPersonList() {
|
||||
listPerson({ pageNum: 1, pageSize: 100 }).then((response) => {
|
||||
personList.value = response.rows || []
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询药品列表 */
|
||||
function getMedicineList() {
|
||||
listMedicineBasic({ pageNum: 1, pageSize: 1000 }).then((response) => {
|
||||
medicineList.value = response.rows || []
|
||||
})
|
||||
}
|
||||
|
||||
// 取消按钮
|
||||
function cancel() {
|
||||
open.value = false
|
||||
reset()
|
||||
}
|
||||
|
||||
// 表单重置
|
||||
function reset() {
|
||||
form.value = {
|
||||
id: null,
|
||||
personId: null,
|
||||
chronicDiseaseId: null,
|
||||
medicineId: null,
|
||||
planName: null,
|
||||
medicineName: null,
|
||||
specification: null,
|
||||
dosagePerTime: 1,
|
||||
dosageUnit: '片',
|
||||
frequencyType: 1,
|
||||
frequencyConfig: null,
|
||||
timeSlots: null,
|
||||
timePointList: ['08:00', '12:00', '18:00'],
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
remindEnabled: 0,
|
||||
remindAdvanceMin: 15,
|
||||
notifyFamily: 0,
|
||||
familyMemberIds: null,
|
||||
familyMemberIdList: [],
|
||||
status: 1,
|
||||
remark: null
|
||||
}
|
||||
chronicDiseaseList.value = []
|
||||
proxy.resetForm('planRef')
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
proxy.resetForm('queryRef')
|
||||
queryParams.value.chronicDiseaseId = null
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
// 分页
|
||||
const handleCurrentChange = (val) => {
|
||||
queryParams.value.pageNum = val
|
||||
getList()
|
||||
}
|
||||
|
||||
const handleSizeChange = (val) => {
|
||||
queryParams.value.pageSize = val
|
||||
getList()
|
||||
}
|
||||
|
||||
// 序号翻页递增
|
||||
const indexMethod = (index) => {
|
||||
const nowPage = queryParams.value.pageNum
|
||||
const nowLimit = queryParams.value.pageSize
|
||||
return index + 1 + (nowPage - 1) * nowLimit
|
||||
}
|
||||
|
||||
// 多选框选中数据
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map((item) => item.id)
|
||||
single.value = selection.length !== 1
|
||||
multiple.value = !selection.length
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
function formatDate(date) {
|
||||
if (!date) return ''
|
||||
// 处理数组格式 [2026, 3, 21]
|
||||
if (Array.isArray(date)) {
|
||||
return `${date[0]}-${String(date[1]).padStart(2, '0')}-${String(date[2]).padStart(2, '0')}`
|
||||
}
|
||||
// 处理字符串格式
|
||||
if (typeof date === 'string') {
|
||||
return date.substring(0, 10)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// 解析服药时间点 JSON
|
||||
function parseTimeSlots(timeSlots) {
|
||||
if (!timeSlots) return []
|
||||
try {
|
||||
return typeof timeSlots === 'string' ? JSON.parse(timeSlots) : timeSlots
|
||||
} catch (e) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// 将数组日期转换为字符串日期
|
||||
function convertArrayDateToString(dateArray) {
|
||||
if (!dateArray) return null
|
||||
if (Array.isArray(dateArray)) {
|
||||
return `${dateArray[0]}-${String(dateArray[1]).padStart(2, '0')}-${String(dateArray[2]).padStart(2, '0')}`
|
||||
}
|
||||
if (typeof dateArray === 'string') {
|
||||
return dateArray.substring(0, 10)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// 查看
|
||||
const handleView = (row) => {
|
||||
getMedicationPlan(row.id).then((response) => {
|
||||
form.value = response.data
|
||||
// 转换日期格式
|
||||
form.value.startDate = convertArrayDateToString(form.value.startDate)
|
||||
form.value.endDate = convertArrayDateToString(form.value.endDate)
|
||||
// 解析 timeSlots JSON
|
||||
if (form.value.timeSlots) {
|
||||
try {
|
||||
form.value.timePointList = typeof form.value.timeSlots === 'string' ? JSON.parse(form.value.timeSlots) : form.value.timeSlots
|
||||
} catch (e) {
|
||||
form.value.timePointList = []
|
||||
}
|
||||
} else {
|
||||
form.value.timePointList = []
|
||||
}
|
||||
// 解析 familyMemberIds JSON
|
||||
if (form.value.familyMemberIds) {
|
||||
try {
|
||||
form.value.familyMemberIdList = typeof form.value.familyMemberIds === 'string' ? JSON.parse(form.value.familyMemberIds) : form.value.familyMemberIds
|
||||
} catch (e) {
|
||||
form.value.familyMemberIdList = []
|
||||
}
|
||||
} else {
|
||||
form.value.familyMemberIdList = []
|
||||
}
|
||||
title.value = '查看用药计划'
|
||||
open.value = true
|
||||
// 加载该人员的慢性疾病列表
|
||||
if (form.value.personId) {
|
||||
listChronicDiseaseByMember(form.value.personId).then((res) => {
|
||||
chronicDiseaseList.value = res.data || []
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 新增按钮操作 */
|
||||
function handleAdd() {
|
||||
reset()
|
||||
open.value = true
|
||||
title.value = '添加用药计划'
|
||||
}
|
||||
|
||||
function handleAddFromChronicDisease() {
|
||||
const personId = route.query.personId ? Number(route.query.personId) : null
|
||||
const chronicDiseaseId = route.query.chronicDiseaseId ? Number(route.query.chronicDiseaseId) : null
|
||||
if (!personId || !chronicDiseaseId) {
|
||||
return
|
||||
}
|
||||
reset()
|
||||
form.value.personId = personId
|
||||
form.value.chronicDiseaseId = chronicDiseaseId
|
||||
form.value.planName = route.query.diseaseName ? `${route.query.diseaseName}用药计划` : null
|
||||
queryParams.value.personId = personId
|
||||
queryParams.value.chronicDiseaseId = chronicDiseaseId
|
||||
listChronicDiseaseByMember(personId).then((res) => {
|
||||
chronicDiseaseList.value = res.data || []
|
||||
})
|
||||
open.value = true
|
||||
title.value = '添加用药计划'
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 修改按钮操作 */
|
||||
function handleUpdate(row) {
|
||||
reset()
|
||||
const _id = row.id || ids.value
|
||||
getMedicationPlan(_id).then((response) => {
|
||||
form.value = response.data
|
||||
// 转换日期格式
|
||||
form.value.startDate = convertArrayDateToString(form.value.startDate)
|
||||
form.value.endDate = convertArrayDateToString(form.value.endDate)
|
||||
// 解析 timeSlots JSON
|
||||
if (form.value.timeSlots) {
|
||||
try {
|
||||
form.value.timePointList = typeof form.value.timeSlots === 'string' ? JSON.parse(form.value.timeSlots) : form.value.timeSlots
|
||||
} catch (e) {
|
||||
form.value.timePointList = []
|
||||
}
|
||||
} else {
|
||||
form.value.timePointList = []
|
||||
}
|
||||
// 解析 familyMemberIds JSON
|
||||
if (form.value.familyMemberIds) {
|
||||
try {
|
||||
form.value.familyMemberIdList = typeof form.value.familyMemberIds === 'string' ? JSON.parse(form.value.familyMemberIds) : form.value.familyMemberIds
|
||||
} catch (e) {
|
||||
form.value.familyMemberIdList = []
|
||||
}
|
||||
} else {
|
||||
form.value.familyMemberIdList = []
|
||||
}
|
||||
open.value = true
|
||||
title.value = '修改用药计划'
|
||||
// 加载该人员的慢性疾病列表
|
||||
if (form.value.personId) {
|
||||
listChronicDiseaseByMember(form.value.personId).then((res) => {
|
||||
chronicDiseaseList.value = res.data || []
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 暂停按钮操作 */
|
||||
function handlePause(row) {
|
||||
proxy.$modal
|
||||
.confirm('确认暂停该用药计划?')
|
||||
.then(() => {
|
||||
return pauseMedicationPlan(row.id)
|
||||
})
|
||||
.then((response) => {
|
||||
getList()
|
||||
proxy.$modal.msgSuccess('暂停成功')
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
/** 恢复按钮操作 */
|
||||
function handleResume(row) {
|
||||
resumeMedicationPlan(row.id).then(() => {
|
||||
getList()
|
||||
proxy.$modal.msgSuccess('恢复成功')
|
||||
})
|
||||
}
|
||||
|
||||
/** 结束按钮操作 */
|
||||
function handleEnd(row) {
|
||||
proxy.$modal
|
||||
.confirm('确认结束该用药计划?结束后将不再生成用药记录。')
|
||||
.then(() => {
|
||||
return endMedicationPlan(row.id)
|
||||
})
|
||||
.then(() => {
|
||||
getList()
|
||||
proxy.$modal.msgSuccess('已结束')
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs.planRef.validate((valid) => {
|
||||
if (valid) {
|
||||
// 构建提交数据,排除后端自动生成的字段
|
||||
const submitData = { ...form.value }
|
||||
// 将时间点列表转为 JSON 字符串
|
||||
submitData.timeSlots = JSON.stringify(form.value.timePointList)
|
||||
// 将家属成员ID列表转为 JSON 字符串
|
||||
if (form.value.familyMemberIdList && form.value.familyMemberIdList.length > 0) {
|
||||
submitData.familyMemberIds = JSON.stringify(form.value.familyMemberIdList)
|
||||
} else {
|
||||
submitData.familyMemberIds = null
|
||||
}
|
||||
// 根据药品ID获取药品名称和规格
|
||||
const medicine = medicineList.value.find((m) => m.id === submitData.medicineId)
|
||||
if (medicine) {
|
||||
submitData.medicineName = medicine.shortName + '-' + medicine.brand
|
||||
submitData.specification = medicine.specification || ''
|
||||
}
|
||||
// 移除后端自动管理的字段
|
||||
delete submitData.createTime
|
||||
delete submitData.updateTime
|
||||
delete submitData.familyMemberIdList
|
||||
if (submitData.id != null) {
|
||||
updateMedicationPlan(submitData).then((response) => {
|
||||
proxy.$modal.msgSuccess('修改成功')
|
||||
open.value = false
|
||||
getList()
|
||||
})
|
||||
} else {
|
||||
addMedicationPlan(submitData).then((response) => {
|
||||
proxy.$modal.msgSuccess('新增成功')
|
||||
open.value = false
|
||||
getList()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(row) {
|
||||
const _ids = row.id || ids.value
|
||||
proxy.$modal
|
||||
.confirm('是否确认删除选中的数据项?')
|
||||
.then(function () {
|
||||
return delMedicationPlan(_ids)
|
||||
})
|
||||
.then(() => {
|
||||
getList()
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
/** 添加服药时间点 */
|
||||
function addTimePoint(val) {
|
||||
if (val && !form.value.timePointList.includes(val)) {
|
||||
form.value.timePointList.push(val)
|
||||
}
|
||||
newTimePoint.value = null
|
||||
}
|
||||
|
||||
/** 移除服药时间点 */
|
||||
function removeTimePoint(index) {
|
||||
form.value.timePointList.splice(index, 1)
|
||||
}
|
||||
|
||||
/** 药品选择变化 */
|
||||
function handleMedicineChange(medicineId) {
|
||||
const medicine = medicineList.value.find((m) => m.id === medicineId)
|
||||
if (medicine) {
|
||||
form.value.dosageUnit = medicine.unit || '片'
|
||||
}
|
||||
}
|
||||
|
||||
/** 人员选择变化 */
|
||||
function handlePersonChange(personId) {
|
||||
form.value.chronicDiseaseId = null
|
||||
chronicDiseaseList.value = []
|
||||
if (personId) {
|
||||
listChronicDiseaseByMember(personId).then((response) => {
|
||||
chronicDiseaseList.value = response.data || []
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
getPersonList()
|
||||
getMedicineList()
|
||||
getList()
|
||||
handleAddFromChronicDisease()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.time-slots-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
background: #fafbfc;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 6px;
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.time-slot-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 10px;
|
||||
background: #fff;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
color: #606266;
|
||||
font-size: 13px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.time-slot-item:hover {
|
||||
border-color: #409eff;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.time-text {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: #c0c4cc;
|
||||
transition: all 0.2s;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.close-icon:hover {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.time-slots-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.time-tag {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
}
|
||||
</style>
|
||||
555
src/views/health/medicationStatistic/index.vue
Normal file
555
src/views/health/medicationStatistic/index.vue
Normal file
@@ -0,0 +1,555 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 查询条件 -->
|
||||
<div class="search-con">
|
||||
<div class="title">查询条件</div>
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="80px">
|
||||
<el-form-item label="人员" prop="personId">
|
||||
<el-select v-model="queryParams.personId" placeholder="请选择人员" clearable filterable>
|
||||
<el-option v-for="item in personList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="统计周期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="search-btn-con">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 概览卡片 -->
|
||||
<div class="overview-cards" v-loading="loading">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon" style="background: #409eff">
|
||||
<el-icon><Calendar /></el-icon>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value">{{ overview.totalTasks || 0 }}</div>
|
||||
<div class="stat-label">总任务数</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon" style="background: #67c23a">
|
||||
<el-icon><Check /></el-icon>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value">{{ overview.completedTasks || 0 }}</div>
|
||||
<div class="stat-label">已服药</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon" style="background: #f56c6c">
|
||||
<el-icon><Close /></el-icon>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value">{{ overview.missedTasks || 0 }}</div>
|
||||
<div class="stat-label">漏服</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon" style="background: #e6a23c">
|
||||
<el-icon><Timer /></el-icon>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value">{{ overview.adherenceRate || 0 }}%</div>
|
||||
<div class="stat-label">服药率</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon" style="background: #909399">
|
||||
<el-icon><Clock /></el-icon>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value">{{ overview.ontimeRate || 0 }}%</div>
|
||||
<div class="stat-label">准时率</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图表区域 -->
|
||||
<div class="charts-row">
|
||||
<!-- 趋势图 -->
|
||||
<div class="chart-card">
|
||||
<div class="chart-title">服药趋势(最近7天)</div>
|
||||
<div ref="trendChartRef" class="chart-container"></div>
|
||||
</div>
|
||||
<!-- 时段统计 -->
|
||||
<div class="chart-card">
|
||||
<div class="chart-title">时段分布</div>
|
||||
<div ref="timeSlotChartRef" class="chart-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 日历视图 -->
|
||||
<div class="calendar-section">
|
||||
<div class="calendar-header">
|
||||
<span class="calendar-title">日历视图</span>
|
||||
<div class="calendar-nav">
|
||||
<el-button-group>
|
||||
<el-button @click="changeMonth(-1)" icon="ArrowLeft">上月</el-button>
|
||||
<el-button>{{ currentMonth }}</el-button>
|
||||
<el-button @click="changeMonth(1)"
|
||||
>下月<el-icon class="el-icon--right"><ArrowRight /></el-icon
|
||||
></el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="calendar-grid" v-loading="calendarLoading">
|
||||
<div class="calendar-weekdays">
|
||||
<div v-for="day in weekdays" :key="day" class="weekday">{{ day }}</div>
|
||||
</div>
|
||||
<div class="calendar-days">
|
||||
<div v-for="(day, index) in calendarDays" :key="index" class="calendar-day" :class="{ 'other-month': day.otherMonth, today: day.isToday }">
|
||||
<div class="day-number">{{ day.day }}</div>
|
||||
<div class="day-stats" v-if="day.data && day.data.totalTasks > 0">
|
||||
<span class="stat-dot completed" :title="`已服药: ${day.data.completedTasks}`"></span>
|
||||
<span class="stat-dot missed" v-if="day.data.missedTasks > 0" :title="`漏服: ${day.data.missedTasks}`"></span>
|
||||
<span class="stat-rate">{{ day.data.adherenceRate }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="MedicationStatistic">
|
||||
import { ref, reactive, onMounted, watch, computed, nextTick } from 'vue'
|
||||
import { getOverallAdherence, getDailyAdherence, getCalendarData, getTimeSlotAdherence } from '@/api/health/medicationAdherence'
|
||||
import { listPerson } from '@/api/health/person'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const loading = ref(false)
|
||||
const calendarLoading = ref(false)
|
||||
const personList = ref([])
|
||||
const queryParams = reactive({
|
||||
personId: null
|
||||
})
|
||||
const dateRange = ref([])
|
||||
const overview = ref({})
|
||||
|
||||
// 日历相关
|
||||
const currentMonth = ref('')
|
||||
const currentYearMonth = ref('')
|
||||
const calendarData = ref([])
|
||||
const weekdays = ['一', '二', '三', '四', '五', '六', '日']
|
||||
|
||||
// 图表
|
||||
const trendChartRef = ref(null)
|
||||
const timeSlotChartRef = ref(null)
|
||||
let trendChart = null
|
||||
let timeSlotChart = null
|
||||
|
||||
// 计算日历天数
|
||||
const calendarDays = computed(() => {
|
||||
const days = []
|
||||
if (!currentYearMonth.value) return days
|
||||
|
||||
const [year, month] = currentYearMonth.value.split('-').map(Number)
|
||||
const firstDay = new Date(year, month - 1, 1)
|
||||
const lastDay = new Date(year, month, 0)
|
||||
const startWeekday = (firstDay.getDay() + 6) % 7 // 周一为0
|
||||
|
||||
// 上月填充
|
||||
const prevMonthLastDay = new Date(year, month - 1, 0).getDate()
|
||||
for (let i = startWeekday - 1; i >= 0; i--) {
|
||||
days.push({
|
||||
day: prevMonthLastDay - i,
|
||||
otherMonth: true,
|
||||
isToday: false,
|
||||
data: null
|
||||
})
|
||||
}
|
||||
|
||||
// 当月
|
||||
const today = new Date()
|
||||
const todayStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`
|
||||
|
||||
const dataMap = {}
|
||||
calendarData.value.forEach((d) => {
|
||||
dataMap[d.date] = d
|
||||
})
|
||||
|
||||
for (let i = 1; i <= lastDay.getDate(); i++) {
|
||||
const dateStr = `${year}-${String(month).padStart(2, '0')}-${String(i).padStart(2, '0')}`
|
||||
days.push({
|
||||
day: i,
|
||||
otherMonth: false,
|
||||
isToday: dateStr === todayStr,
|
||||
data: dataMap[dateStr] || null
|
||||
})
|
||||
}
|
||||
|
||||
// 下月填充
|
||||
const remaining = 42 - days.length
|
||||
for (let i = 1; i <= remaining; i++) {
|
||||
days.push({
|
||||
day: i,
|
||||
otherMonth: true,
|
||||
isToday: false,
|
||||
data: null
|
||||
})
|
||||
}
|
||||
|
||||
return days
|
||||
})
|
||||
|
||||
// 获取人员列表
|
||||
const getPersonList = async () => {
|
||||
try {
|
||||
const res = await listPerson({})
|
||||
personList.value = res.rows || []
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
// 查询统计数据
|
||||
const getStatistics = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const params = { ...queryParams }
|
||||
if (dateRange.value && dateRange.value.length === 2) {
|
||||
params.startDate = dateRange.value[0]
|
||||
params.endDate = dateRange.value[1]
|
||||
} else {
|
||||
// 默认最近7天
|
||||
const today = new Date()
|
||||
const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000)
|
||||
params.startDate = weekAgo.toISOString().split('T')[0]
|
||||
params.endDate = today.toISOString().split('T')[0]
|
||||
}
|
||||
|
||||
// 获取总体统计
|
||||
const overviewRes = await getOverallAdherence(params)
|
||||
overview.value = overviewRes.data || {}
|
||||
|
||||
// 获取每日趋势
|
||||
const dailyRes = await getDailyAdherence(params)
|
||||
renderTrendChart(dailyRes.data || [])
|
||||
|
||||
// 获取时段统计
|
||||
const timeSlotRes = await getTimeSlotAdherence(params)
|
||||
renderTimeSlotChart(timeSlotRes.data || {})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染趋势图
|
||||
const renderTrendChart = (data) => {
|
||||
nextTick(() => {
|
||||
if (!trendChartRef.value) return
|
||||
if (!trendChart) {
|
||||
trendChart = echarts.init(trendChartRef.value)
|
||||
}
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: ['服药率', '准时率']
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: data.map((d) => d.date?.substring(5) || '')
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
max: 100
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '服药率',
|
||||
type: 'line',
|
||||
data: data.map((d) => d.adherenceRate || 0),
|
||||
smooth: true,
|
||||
itemStyle: { color: '#409EFF' }
|
||||
},
|
||||
{
|
||||
name: '准时率',
|
||||
type: 'line',
|
||||
data: data.map((d) => d.ontimeRate || 0),
|
||||
smooth: true,
|
||||
itemStyle: { color: '#67C23A' }
|
||||
}
|
||||
]
|
||||
}
|
||||
trendChart.setOption(option)
|
||||
})
|
||||
}
|
||||
|
||||
// 渲染时段统计图
|
||||
const renderTimeSlotChart = (data) => {
|
||||
nextTick(() => {
|
||||
if (!timeSlotChartRef.value) return
|
||||
if (!timeSlotChart) {
|
||||
timeSlotChart = echarts.init(timeSlotChartRef.value)
|
||||
}
|
||||
|
||||
const slotNames = {
|
||||
morning: '上午(6-12)',
|
||||
afternoon: '下午(12-18)',
|
||||
evening: '晚间(18-24)',
|
||||
night: '凌晨(0-6)'
|
||||
}
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c}%'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
data: Object.entries(data).map(([key, value]) => ({
|
||||
name: slotNames[key] || key,
|
||||
value: value.adherenceRate || 0
|
||||
})),
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
timeSlotChart.setOption(option)
|
||||
})
|
||||
}
|
||||
|
||||
// 获取日历数据
|
||||
const getCalendar = async () => {
|
||||
calendarLoading.value = true
|
||||
try {
|
||||
const res = await getCalendarData(queryParams.personId, currentYearMonth.value)
|
||||
calendarData.value = res.data || []
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
calendarLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 切换月份
|
||||
const changeMonth = (delta) => {
|
||||
const [year, month] = currentYearMonth.value.split('-').map(Number)
|
||||
const newDate = new Date(year, month - 1 + delta, 1)
|
||||
currentYearMonth.value = `${newDate.getFullYear()}-${String(newDate.getMonth() + 1).padStart(2, '0')}`
|
||||
currentMonth.value = `${newDate.getFullYear()}年${newDate.getMonth() + 1}月`
|
||||
getCalendar()
|
||||
}
|
||||
|
||||
const handleQuery = () => {
|
||||
getStatistics()
|
||||
getCalendar()
|
||||
}
|
||||
|
||||
const resetQuery = () => {
|
||||
queryParams.personId = null
|
||||
dateRange.value = []
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化日期
|
||||
const today = new Date()
|
||||
currentYearMonth.value = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}`
|
||||
currentMonth.value = `${today.getFullYear()}年${today.getMonth() + 1}月`
|
||||
|
||||
getPersonList()
|
||||
getStatistics()
|
||||
getCalendar()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.app-container {
|
||||
height: calc(100vh - 84px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.overview-cards {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
min-width: 180px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
.stat-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
.stat-value {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
}
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.charts-row {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chart-card {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
.chart-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-section {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
.calendar-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.calendar-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-grid {
|
||||
.calendar-weekdays {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
text-align: center;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.weekday {
|
||||
padding: 8px;
|
||||
color: #909399;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-days {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: 4px;
|
||||
|
||||
.calendar-day {
|
||||
min-height: 80px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
|
||||
&.other-month {
|
||||
background: #f5f7fa;
|
||||
.day-number {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
|
||||
&.today {
|
||||
border-color: #409eff;
|
||||
.day-number {
|
||||
color: #409eff;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.day-number {
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.day-stats {
|
||||
margin-top: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
.stat-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
|
||||
&.completed {
|
||||
background: #67c23a;
|
||||
}
|
||||
&.missed {
|
||||
background: #f56c6c;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-rate {
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
298
src/views/health/medicationTask/index.vue
Normal file
298
src/views/health/medicationTask/index.vue
Normal file
@@ -0,0 +1,298 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="search-con">
|
||||
<div class="title">查询条件</div>
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="人员" prop="memberId">
|
||||
<el-select v-model="queryParams.memberId" placeholder="请选择人员" clearable filterable>
|
||||
<el-option v-for="item in memberList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="计划" prop="planId">
|
||||
<el-select v-model="queryParams.planId" placeholder="请选择计划" clearable filterable style="width: 250px">
|
||||
<el-option v-for="item in planList" :key="item.id" :label="item.planName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
|
||||
<el-option v-for="dict in medication_task_status" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="计划日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="search-btn-con">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main-con">
|
||||
<div class="title-con">
|
||||
<div class="title">服药任务</div>
|
||||
<div class="operate-btn-con">
|
||||
<el-button :disabled="multiple" icon="Delete" @click="handleDelete" v-hasPermi="['health:medicationTask:remove']">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-con" v-loading="loading">
|
||||
<el-table v-loading="loading" :data="taskList" @selection-change="handleSelectionChange" height="calc(100% - 0.65rem)">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column type="index" label="序号" :index="indexMethod" width="50"></el-table-column>
|
||||
<el-table-column label="计划名称" align="center" prop="planName" width="150" />
|
||||
<el-table-column label="人员" align="center" prop="personName" width="80" />
|
||||
<el-table-column label="药品" align="center" prop="medicineName" width="180" />
|
||||
<el-table-column label="计划日期" align="center" prop="plannedDate" width="110">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.plannedDate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="计划时间" align="center" prop="plannedTime" width="100">
|
||||
<template #default="scope">
|
||||
{{ formatTime(scope.row.plannedTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实际时间" align="center" width="160">
|
||||
<template #default="scope">
|
||||
{{ scope.row.actualTime ? formatDateTime(scope.row.actualTime) : '--' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="剂量" align="center" width="100">
|
||||
<template #default="scope">
|
||||
{{ scope.row.plannedDosage }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status" width="90">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="medication_task_status" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="notes" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<div class="ctrl-btn d-flex">
|
||||
<el-tooltip v-if="scope.row.status === 0" class="item" effect="dark" content="确认服药" placement="top">
|
||||
<el-button type="success" icon="Check" v-hasPermi="['health:medicationTask:edit']" circle @click="handleConfirm(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.status === 0" class="item" effect="dark" content="跳过" placement="top">
|
||||
<el-button type="warning" icon="Close" v-hasPermi="['health:medicationTask:edit']" circle @click="handleSkip(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" effect="dark" content="删除" placement="top">
|
||||
<el-button icon="Delete" v-hasPermi="['health:medicationTask:remove']" circle @click="handleDelete(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
small
|
||||
background
|
||||
layout="total,sizes, prev, pager, next"
|
||||
:page-sizes="[10, 20, 50]"
|
||||
:total="total"
|
||||
v-model:current-page="queryParams.pageNum"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
@size-change="getList"
|
||||
@current-change="getList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 确认服药对话框 -->
|
||||
<el-dialog title="确认服药" v-model="confirmDialogVisible" width="400px">
|
||||
<el-form>
|
||||
<el-form-item label="实际服药时间">
|
||||
<el-time-picker v-model="actualTime" placeholder="选择实际服药时间" format="HH:mm" value-format="HH:mm" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="confirmDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitConfirm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 跳过对话框 -->
|
||||
<el-dialog title="跳过服药" v-model="skipDialogVisible" width="400px">
|
||||
<el-form>
|
||||
<el-form-item label="跳过原因">
|
||||
<el-input v-model="skipNotes" type="textarea" placeholder="请输入跳过原因(选填)" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="skipDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitSkip">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="MedicationTask">
|
||||
import { listMedicationTask, delMedicationTask, confirmMedication, skipMedication } from '@/api/health/medicationTask'
|
||||
import { listMedicationPlan } from '@/api/health/medicationPlan'
|
||||
import { listPerson } from '@/api/health/person'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { medication_task_status } = proxy.useDict('medication_task_status')
|
||||
|
||||
const taskList = ref([])
|
||||
const planList = ref([])
|
||||
const memberList = ref([])
|
||||
const loading = ref(false)
|
||||
const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const dateRange = ref([])
|
||||
const ids = ref([])
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
memberId: null,
|
||||
planId: null,
|
||||
status: null,
|
||||
plannedDateBegin: null,
|
||||
plannedDateEnd: null
|
||||
})
|
||||
|
||||
const confirmDialogVisible = ref(false)
|
||||
const actualTime = ref('')
|
||||
const skipDialogVisible = ref(false)
|
||||
const skipNotes = ref('')
|
||||
const currentRow = ref(null)
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
if (dateRange.value && dateRange.value.length === 2) {
|
||||
queryParams.plannedDateBegin = dateRange.value[0]
|
||||
queryParams.plannedDateEnd = dateRange.value[1]
|
||||
} else {
|
||||
queryParams.plannedDateBegin = null
|
||||
queryParams.plannedDateEnd = null
|
||||
}
|
||||
try {
|
||||
const res = await listMedicationTask(queryParams)
|
||||
taskList.value = res.rows || []
|
||||
total.value = res.total || 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const getPlanList = async () => {
|
||||
const res = await listMedicationPlan({})
|
||||
planList.value = res.rows || []
|
||||
}
|
||||
|
||||
const getMemberList = async () => {
|
||||
const res = await listPerson({})
|
||||
memberList.value = res.rows || []
|
||||
}
|
||||
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
const resetQuery = () => {
|
||||
dateRange.value = []
|
||||
queryParams.memberId = null
|
||||
queryParams.planId = null
|
||||
queryParams.status = null
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
const handleSelectionChange = (selection) => {
|
||||
ids.value = selection.map((item) => item.id)
|
||||
multiple.value = !selection.length
|
||||
}
|
||||
|
||||
const handleConfirm = (row) => {
|
||||
currentRow.value = row
|
||||
// 默认当前时间
|
||||
const now = new Date()
|
||||
actualTime.value = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
|
||||
confirmDialogVisible.value = true
|
||||
}
|
||||
|
||||
const submitConfirm = async () => {
|
||||
if (!actualTime.value) {
|
||||
proxy.$modal.msgError('请选择实际服药时间')
|
||||
return
|
||||
}
|
||||
// confirmType=1 表示确认服药,confirmTime 传递时间字符串
|
||||
await confirmMedication(currentRow.value.id, 1, actualTime.value)
|
||||
proxy.$modal.msgSuccess('操作成功')
|
||||
confirmDialogVisible.value = false
|
||||
getList()
|
||||
}
|
||||
|
||||
const handleSkip = (row) => {
|
||||
currentRow.value = row
|
||||
skipNotes.value = ''
|
||||
skipDialogVisible.value = true
|
||||
}
|
||||
|
||||
const submitSkip = async () => {
|
||||
await skipMedication(currentRow.value.id, skipNotes.value)
|
||||
proxy.$modal.msgSuccess('操作成功')
|
||||
skipDialogVisible.value = false
|
||||
getList()
|
||||
}
|
||||
|
||||
const handleDelete = async (row) => {
|
||||
const idList = row.id ? [row.id] : ids.value
|
||||
await proxy.$modal.confirm('确认删除选中的记录?')
|
||||
await delMedicationTask(idList.join(','))
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
getList()
|
||||
}
|
||||
|
||||
const indexMethod = (index) => {
|
||||
return (queryParams.pageNum - 1) * queryParams.pageSize + index + 1
|
||||
}
|
||||
|
||||
const formatDate = (date) => {
|
||||
if (!date) return '--'
|
||||
// 处理数组格式 [2026, 3, 21]
|
||||
if (Array.isArray(date)) {
|
||||
return `${date[0]}-${String(date[1]).padStart(2, '0')}-${String(date[2]).padStart(2, '0')}`
|
||||
}
|
||||
// 处理字符串格式
|
||||
if (typeof date === 'string') {
|
||||
return date.substring(0, 10)
|
||||
}
|
||||
return '--'
|
||||
}
|
||||
|
||||
const formatTime = (time) => {
|
||||
if (!time) return '--'
|
||||
// 处理数组格式 [8, 0] 或 [14, 30]
|
||||
if (Array.isArray(time)) {
|
||||
return `${String(time[0]).padStart(2, '0')}:${String(time[1]).padStart(2, '0')}`
|
||||
}
|
||||
// 处理字符串格式
|
||||
if (typeof time === 'string') {
|
||||
return time.substring(0, 5)
|
||||
}
|
||||
return '--'
|
||||
}
|
||||
|
||||
const formatDateTime = (datetime) => {
|
||||
if (!datetime) return '--'
|
||||
return datetime.replace('T', ' ').substring(0, 16)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
getPlanList()
|
||||
getMemberList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -79,6 +79,28 @@
|
||||
<el-table-column label="包装" align="center" prop="packaging" width="150" />
|
||||
<el-table-column label="品牌" align="center" prop="brand" width="120" />
|
||||
<el-table-column label="生产厂家" align="center" prop="manufacturers" />
|
||||
<el-table-column label="附件" align="center" prop="attachment" width="120">
|
||||
<template #default="scope">
|
||||
<el-popover v-if="parseAttachments(scope.row.attachment).length" placement="left" width="280" trigger="click">
|
||||
<template #reference>
|
||||
<el-button link type="primary">查看{{ parseAttachments(scope.row.attachment).length }}个</el-button>
|
||||
</template>
|
||||
<div class="attachment-popover-list">
|
||||
<el-link
|
||||
v-for="(file, index) in parseAttachments(scope.row.attachment)"
|
||||
:key="file"
|
||||
type="primary"
|
||||
:href="getAttachmentUrl(file)"
|
||||
target="_blank"
|
||||
:underline="false"
|
||||
>
|
||||
{{ getAttachmentName(file, index) }}
|
||||
</el-link>
|
||||
</div>
|
||||
</el-popover>
|
||||
<span v-else class="attachment-empty">无</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
@@ -190,6 +212,22 @@
|
||||
<el-form-item label="不良反应" prop="adverseReaction" style="width: 1200px">
|
||||
<el-input v-model="form.adverseReaction" type="textarea" placeholder="请输入不良反应" />
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" style="width: 1200px" prop="attachment">
|
||||
<div v-if="title === '查看药品基础信息'" class="attachment-readonly-list attachment-form-list">
|
||||
<el-link
|
||||
v-for="(file, index) in parseAttachments(form.attachment)"
|
||||
:key="file"
|
||||
type="primary"
|
||||
:href="getAttachmentUrl(file)"
|
||||
target="_blank"
|
||||
:underline="false"
|
||||
>
|
||||
{{ getAttachmentName(file, index) }}
|
||||
</el-link>
|
||||
<span v-if="!parseAttachments(form.attachment).length" class="attachment-empty">暂无附件</span>
|
||||
</div>
|
||||
<file-upload v-else v-model="form.attachment" :limit="9" :file-size="10" :file-type="attachmentFileTypes" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" style="width: 1200px" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
@@ -229,6 +267,7 @@ const single = ref(true)
|
||||
const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const title = ref('')
|
||||
const attachmentFileTypes = ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx']
|
||||
const operateList = ref([
|
||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['health:medicineBasic:query'] },
|
||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['health:medicineBasic:edit'] },
|
||||
@@ -289,6 +328,39 @@ const handleOperate = (operate, row) => {
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data)
|
||||
|
||||
function parseAttachments(attachment) {
|
||||
if (!attachment) {
|
||||
return []
|
||||
}
|
||||
return String(attachment)
|
||||
.split(',')
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
function getAttachmentUrl(file) {
|
||||
if (!file) {
|
||||
return ''
|
||||
}
|
||||
if (/^(https?:)?\/\//.test(file) || file.startsWith('/') || file.startsWith('blob:') || file.startsWith('data:')) {
|
||||
return file
|
||||
}
|
||||
return `/${file}`
|
||||
}
|
||||
|
||||
function getAttachmentName(file, index) {
|
||||
const purePath = String(file || '').split('?')[0]
|
||||
const name = purePath.slice(purePath.lastIndexOf('/') + 1)
|
||||
if (!name) {
|
||||
return `附件${index + 1}`
|
||||
}
|
||||
try {
|
||||
return decodeURIComponent(name)
|
||||
} catch {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询药品基础信息列表 */
|
||||
function getList() {
|
||||
loading.value = true
|
||||
@@ -347,7 +419,8 @@ function reset() {
|
||||
contentUnit: null,
|
||||
character: null,
|
||||
storage: null,
|
||||
indications: null
|
||||
indications: null,
|
||||
attachment: null
|
||||
}
|
||||
proxy.resetForm('medicineBasicRef')
|
||||
}
|
||||
@@ -463,3 +536,23 @@ function handleExport() {
|
||||
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.attachment-popover-list,
|
||||
.attachment-readonly-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.attachment-form-list {
|
||||
min-height: 32px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.attachment-empty {
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
|
||||
2039
src/views/health/recordScreen/index.vue
Normal file
2039
src/views/health/recordScreen/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,121 @@
|
||||
<template>
|
||||
<h1>首页</h1>
|
||||
<section class="welcome-page">
|
||||
<div class="welcome-content">
|
||||
<p class="eyebrow">WELCOME</p>
|
||||
<h1>欢迎使用智聪综合管理平台</h1>
|
||||
<p class="subtitle">让数据管理更清晰,让日常协作更高效。</p>
|
||||
<div class="welcome-notes">
|
||||
<span>统一入口</span>
|
||||
<span>高效管理</span>
|
||||
<span>稳定运行</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup name="Index"></script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style scoped lang="scss">
|
||||
.welcome-page {
|
||||
position: relative;
|
||||
min-height: calc(100vh - 84px);
|
||||
padding: 64px;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(90deg, rgba(8, 24, 54, 0.94), rgba(10, 47, 92, 0.82) 46%, rgba(15, 82, 128, 0.54)),
|
||||
linear-gradient(135deg, rgba(41, 171, 226, 0.28) 0 1px, transparent 1px 42px), linear-gradient(45deg, rgba(255, 255, 255, 0.08) 0 1px, transparent 1px 58px),
|
||||
radial-gradient(circle at 82% 18%, rgba(81, 198, 255, 0.42), transparent 28%), radial-gradient(circle at 72% 72%, rgba(76, 220, 178, 0.22), transparent 34%),
|
||||
#071832;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.welcome-page::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 8%;
|
||||
top: 18%;
|
||||
width: 420px;
|
||||
height: 420px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.16);
|
||||
border-radius: 50%;
|
||||
box-shadow: inset 0 0 0 42px rgba(255, 255, 255, 0.03), inset 0 0 0 108px rgba(255, 255, 255, 0.025), 0 0 48px rgba(44, 180, 255, 0.18);
|
||||
}
|
||||
|
||||
.welcome-page::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 12%;
|
||||
bottom: 14%;
|
||||
width: 34%;
|
||||
max-width: 520px;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.52), transparent);
|
||||
transform: rotate(-18deg);
|
||||
}
|
||||
|
||||
.welcome-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 720px;
|
||||
padding-top: 9vh;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
margin: 0 0 18px;
|
||||
color: rgba(255, 255, 255, 0.72);
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.18em;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 44px;
|
||||
font-weight: 800;
|
||||
line-height: 1.22;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
max-width: 520px;
|
||||
margin: 22px 0 0;
|
||||
color: rgba(255, 255, 255, 0.78);
|
||||
font-size: 18px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.welcome-notes {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-top: 34px;
|
||||
|
||||
span {
|
||||
padding: 10px 18px;
|
||||
color: rgba(255, 255, 255, 0.88);
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
border: 1px solid rgba(255, 255, 255, 0.22);
|
||||
border-radius: 6px;
|
||||
backdrop-filter: blur(8px);
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.welcome-page {
|
||||
min-height: calc(100vh - 50px);
|
||||
padding: 36px 22px;
|
||||
}
|
||||
|
||||
.welcome-content {
|
||||
padding-top: 7vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1722,12 +1722,15 @@ const loadAllData = async () => {
|
||||
weeklyExpenseData.value = weekDates.map((date) => ({ date, value: 0 }))
|
||||
}
|
||||
|
||||
// 处理近一周投资收益数据(使用 tableAccountsList 字段)
|
||||
// 处理近一周投资收益数据(使用 tableAccountsList 字段,倒序后按时间正序展示)
|
||||
if (weeklyInvestRes && weeklyInvestRes.data && weeklyInvestRes.data.tableAccountsList) {
|
||||
weeklyInvestIncomeData.value = weeklyInvestRes.data.tableAccountsList.map((item) => ({
|
||||
date: item.time.substring(5), // 只显示 MM-DD
|
||||
value: Number(item.value) || 0
|
||||
}))
|
||||
weeklyInvestIncomeData.value = weeklyInvestRes.data.tableAccountsList
|
||||
.slice()
|
||||
.reverse()
|
||||
.map((item) => ({
|
||||
date: item.time.substring(5), // 只显示 MM-DD
|
||||
value: Number(item.value) || 0
|
||||
}))
|
||||
} else {
|
||||
weeklyInvestIncomeData.value = weekDates.map((date) => ({ date, value: 0 }))
|
||||
}
|
||||
@@ -1784,12 +1787,15 @@ const loadAllData = async () => {
|
||||
monthlyExpenseData.value = []
|
||||
}
|
||||
|
||||
// 处理近一月投资收益数据(使用 tableAccountsList 字段)
|
||||
// 处理近一月投资收益数据(使用 tableAccountsList 字段,倒序后按时间正序展示)
|
||||
if (monthlyInvestRes && monthlyInvestRes.data && monthlyInvestRes.data.tableAccountsList) {
|
||||
monthlyInvestIncomeData.value = monthlyInvestRes.data.tableAccountsList.map((item) => ({
|
||||
date: item.time.substring(5), // 只显示 MM-DD
|
||||
value: Number(item.value) || 0
|
||||
}))
|
||||
monthlyInvestIncomeData.value = monthlyInvestRes.data.tableAccountsList
|
||||
.slice()
|
||||
.reverse()
|
||||
.map((item) => ({
|
||||
date: item.time.substring(5), // 只显示 MM-DD
|
||||
value: Number(item.value) || 0
|
||||
}))
|
||||
} else {
|
||||
monthlyInvestIncomeData.value = []
|
||||
}
|
||||
|
||||
1870
src/views/invest/accountDealLocationScreen/index.vue
Normal file
1870
src/views/invest/accountDealLocationScreen/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -149,20 +149,21 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loadingDealRecord" :data="tableDealRecordData">
|
||||
<el-table-column label="交易时间" align="center" prop="createTime" />
|
||||
<el-table-column label="交易时间" align="center" width="180" prop="createTime" />
|
||||
<el-table-column label="交易金额" align="center" prop="amount" />
|
||||
<el-table-column label="当前余额" align="center" prop="currentBalance" />
|
||||
<el-table-column label="交易类型" align="center" prop="dealType">
|
||||
<el-table-column label="交易类型" align="center" width="120" prop="dealType">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="deal_type" :value="scope.row.dealType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易类别" align="center" prop="dealCategory">
|
||||
<el-table-column label="交易类别" align="center" width="150" prop="dealCategory">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="deal_category" :value="scope.row.dealCategory" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" width="380" prop="remark" />
|
||||
<el-table-column label="交易子类别" align="center" width="150" prop="childCategoryName" />
|
||||
<el-table-column label="备注" align="center" width="300" prop="remark" />
|
||||
</el-table>
|
||||
<el-pagination small background layout="total, prev, pager, next" :total="dealRecordTotal" @current-change="handleCurrentDealRecordChange" />
|
||||
</el-dialog>
|
||||
@@ -266,7 +267,7 @@ const operateList = ref([
|
||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['invest:accounts:query'] },
|
||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['invest:accounts:edit'] },
|
||||
// { id: 'delete', icon: 'Delete', title: '删除', hasPermi: ['invest:accounts:remove'] },
|
||||
{ id: 'dealRecord', icon: 'Tickets', title: '交易记录', hasPermi: ['invest:accounts:query'] },
|
||||
{ id: 'dealRecord', icon: 'Tickets', title: '交易明细', hasPermi: ['invest:accounts:query'] },
|
||||
{ id: 'transDeal', icon: 'Cellphone', title: '转账记录', hasPermi: ['invest:accounts:query'] }
|
||||
])
|
||||
const data = reactive({
|
||||
@@ -465,13 +466,13 @@ function handleExport() {
|
||||
)
|
||||
}
|
||||
|
||||
/** 历史数据按钮操作 */
|
||||
/** 交易明细按钮操作 */
|
||||
function handleDealRecord(row) {
|
||||
const _id = row.id || ids.value
|
||||
queryDealRecordParams.value.time = ''
|
||||
queryDealRecordParams.value.dealType = ''
|
||||
queryDealRecordParams.value.dealCategory = ''
|
||||
titleDealRecord.value = row.nameCode + '历史交易记录'
|
||||
titleDealRecord.value = `${row.name}${row.code ? '【' + row.code + '】' : ''}交易明细`
|
||||
currentAccountId.value = _id
|
||||
getDealRecordList(1)
|
||||
openDealRecord.value = true
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
<dict-tag :options="account_status" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="165" align="center" class-name="small-padding fixed-width">
|
||||
<el-table-column label="操作" width="210" align="center" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<div class="ctrl-btn d-flex">
|
||||
<el-tooltip v-for="item in operateList" :key="item.id" class="item" effect="dark" :content="item.title" placement="top">
|
||||
@@ -265,6 +265,46 @@
|
||||
</div>
|
||||
<template #footer> </template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :title="titleDealRecord" v-model="openDealRecord" width="1200px" append-to-body destroy-on-close>
|
||||
<el-form :model="queryDealRecordParams" ref="queryDealRecordRef" inline>
|
||||
<el-form-item label="交易时间" prop="time">
|
||||
<el-date-picker v-model="queryDealRecordParams.time" type="daterange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" />
|
||||
</el-form-item>
|
||||
<el-form-item label="交易类型" style="width: 220px" prop="dealType">
|
||||
<el-select v-model="queryDealRecordParams.dealType" placeholder="请选择交易类型" clearable>
|
||||
<el-option v-for="dict in deal_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="交易类别" style="width: 220px" prop="dealCategory">
|
||||
<el-select v-model="queryDealRecordParams.dealCategory" placeholder="请选择交易类别" clearable>
|
||||
<el-option v-for="dict in deal_category" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="">
|
||||
<el-button type="primary" icon="Search" @click="handleDealRecordQuery">搜索</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="resetDealRecordQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loadingDealRecord" :data="tableDealRecordData">
|
||||
<el-table-column label="交易时间" align="center" prop="createTime" width="180" />
|
||||
<el-table-column label="交易金额" align="center" prop="amount" />
|
||||
<el-table-column label="当前余额" align="center" prop="currentBalance" />
|
||||
<el-table-column label="交易类型" width="120" align="center" prop="dealType">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="deal_type" :value="scope.row.dealType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易类别" width="150" align="center" prop="dealCategory">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="deal_category" :value="scope.row.dealCategory" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易子类别" width="150" align="center" prop="childCategoryName" />
|
||||
<el-table-column label="备注" align="center" width="300" prop="remark" />
|
||||
</el-table>
|
||||
<el-pagination small background layout="total, prev, pager, next" :total="dealRecordTotal" @current-change="handleCurrentDealRecordChange" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -272,11 +312,14 @@
|
||||
import { listBankcardLend, getBankcardLend, delBankcardLend, addBankcardLend, updateBankcardLend } from '@/api/invest/bankcardlend'
|
||||
import { listLimitHistory, getLimitHistory, delLimitHistory, addLimitHistory, updateLimitHistory } from '@/api/invest/limitHistory'
|
||||
import { listBankBaseInfo } from '@/api/invest/bankBaseInfo'
|
||||
import { listAccountDealRecord } from '@/api/invest/accountDealRecord'
|
||||
import dayjs from 'dayjs'
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { require } from '@/utils/require'
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { is_zero_bill, is_next_bill_date, account_status, card_tier } = proxy.useDict('is_zero_bill', 'is_next_bill_date', 'account_status', 'card_tier')
|
||||
const { limit_history_type, adjust_type } = proxy.useDict('limit_history_type', 'adjust_type')
|
||||
const { deal_type, deal_category } = proxy.useDict('deal_type', 'deal_category')
|
||||
const bankcardList = ref([])
|
||||
const open = ref(false)
|
||||
const loading = ref(true)
|
||||
@@ -286,6 +329,19 @@ const single = ref(true)
|
||||
const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const title = ref('')
|
||||
const titleDealRecord = ref('')
|
||||
const openDealRecord = ref(false)
|
||||
const currentAccountId = ref('')
|
||||
const loadingDealRecord = ref(false)
|
||||
const tableDealRecordData = ref([])
|
||||
const dealRecordTotal = ref(0)
|
||||
const queryDealRecordParams = ref({
|
||||
time: '',
|
||||
dealType: null,
|
||||
dealCategory: null,
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
|
||||
const detailOpen = ref(false)
|
||||
const openDetail = ref(false)
|
||||
@@ -297,6 +353,7 @@ const bankList = ref([])
|
||||
|
||||
const operateList = ref([
|
||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['invest:bankcard:query'] },
|
||||
{ id: 'dealRecord', icon: 'Tickets', title: '交易明细', hasPermi: ['invest:bankcard:query'] },
|
||||
{ id: 'limit', icon: 'Grid', title: '调额记录', hasPermi: ['invest:bankcard:edit'] },
|
||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['invest:bankcard:edit'] },
|
||||
{ id: 'delete', icon: 'Delete', title: '删除', hasPermi: ['invest:bankcard:remove'] }
|
||||
@@ -339,6 +396,9 @@ const data = reactive({
|
||||
|
||||
const handleOperate = (operate, row) => {
|
||||
switch (operate) {
|
||||
case 'dealRecord':
|
||||
handleDealRecord(row)
|
||||
break
|
||||
case 'limit':
|
||||
handleDetail(row)
|
||||
break
|
||||
@@ -474,6 +534,54 @@ function handleDetail(row) {
|
||||
})
|
||||
}
|
||||
|
||||
/** 交易明细按钮操作 */
|
||||
function handleDealRecord(row) {
|
||||
queryDealRecordParams.value.time = ''
|
||||
queryDealRecordParams.value.dealType = ''
|
||||
queryDealRecordParams.value.dealCategory = ''
|
||||
titleDealRecord.value = `${row.name}${row.code ? '【' + row.code + '】' : ''}交易明细`
|
||||
currentAccountId.value = row.id || ids.value
|
||||
getDealRecordList(1)
|
||||
openDealRecord.value = true
|
||||
}
|
||||
|
||||
const handleCurrentDealRecordChange = (num) => {
|
||||
getDealRecordList(num)
|
||||
}
|
||||
|
||||
const handleDealRecordQuery = () => {
|
||||
getDealRecordList(1)
|
||||
}
|
||||
|
||||
const resetDealRecordQuery = () => {
|
||||
proxy.resetForm('queryDealRecordRef')
|
||||
queryDealRecordParams.value.time = ''
|
||||
queryDealRecordParams.value.dealType = ''
|
||||
queryDealRecordParams.value.dealCategory = ''
|
||||
dealRecordTotal.value = 0
|
||||
getDealRecordList(1)
|
||||
}
|
||||
|
||||
const getDealRecordList = (num) => {
|
||||
loadingDealRecord.value = true
|
||||
const timeRange = queryDealRecordParams.value.time
|
||||
let st = ''
|
||||
let et = ''
|
||||
if (timeRange && timeRange.length === 2) {
|
||||
st = dayjs(timeRange[0]).format('YYYY-MM-DD')
|
||||
et = dayjs(timeRange[1]).format('YYYY-MM-DD')
|
||||
}
|
||||
queryDealRecordParams.value.startTime = st
|
||||
queryDealRecordParams.value.endTime = et
|
||||
queryDealRecordParams.value.pageNum = num
|
||||
queryDealRecordParams.value.accountId = currentAccountId.value
|
||||
listAccountDealRecord(queryDealRecordParams.value).then((response) => {
|
||||
loadingDealRecord.value = false
|
||||
tableDealRecordData.value = response.rows
|
||||
dealRecordTotal.value = response.total
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询信用卡信息列表 */
|
||||
function getList() {
|
||||
loading.value = true
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="卡面" align="center" width="100" prop="cardFace" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<el-table-column label="操作" width="210" align="center" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<div class="ctrl-btn d-flex">
|
||||
<el-tooltip v-for="item in operateList" :key="item.id" class="item" effect="dark" :content="item.title" placement="top">
|
||||
@@ -224,6 +224,46 @@
|
||||
</div>
|
||||
<template #footer> </template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :title="titleDealRecord" v-model="openDealRecord" width="1200px" append-to-body destroy-on-close>
|
||||
<el-form :model="queryDealRecordParams" ref="queryDealRecordRef" inline>
|
||||
<el-form-item label="交易时间" prop="time">
|
||||
<el-date-picker v-model="queryDealRecordParams.time" type="daterange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" />
|
||||
</el-form-item>
|
||||
<el-form-item label="交易类型" style="width: 220px" prop="dealType">
|
||||
<el-select v-model="queryDealRecordParams.dealType" placeholder="请选择交易类型" clearable>
|
||||
<el-option v-for="dict in deal_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="交易类别" style="width: 220px" prop="dealCategory">
|
||||
<el-select v-model="queryDealRecordParams.dealCategory" placeholder="请选择交易类别" clearable>
|
||||
<el-option v-for="dict in deal_category" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="">
|
||||
<el-button type="primary" icon="Search" @click="handleDealRecordQuery">搜索</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="resetDealRecordQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loadingDealRecord" :data="tableDealRecordData">
|
||||
<el-table-column label="交易时间" align="center" prop="createTime" width="180" />
|
||||
<el-table-column label="交易金额" align="center" prop="amount" />
|
||||
<el-table-column label="当前余额" align="center" prop="currentBalance" />
|
||||
<el-table-column label="交易类型" align="center" width="120" prop="dealType">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="deal_type" :value="scope.row.dealType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易类别" align="center" width="120" prop="dealCategory">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="deal_category" :value="scope.row.dealCategory" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易子类别" align="center" width="150" prop="childCategoryName" />
|
||||
<el-table-column label="备注" align="center" width="300" prop="remark" />
|
||||
</el-table>
|
||||
<el-pagination small background layout="total, prev, pager, next" :total="dealRecordTotal" @current-change="handleCurrentDealRecordChange" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -231,11 +271,14 @@
|
||||
import { listBankcardLend, getBankcardLend, delBankcardLend, addBankcardLend, updateBankcardLend } from '@/api/invest/bankcardlend'
|
||||
import { listLimitHistory, getLimitHistory, delLimitHistory, addLimitHistory, updateLimitHistory } from '@/api/invest/limitHistory'
|
||||
import { listBankBaseInfo } from '@/api/invest/bankBaseInfo'
|
||||
import { listAccountDealRecord } from '@/api/invest/accountDealRecord'
|
||||
import dayjs from 'dayjs'
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { require } from '@/utils/require'
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { bank_card_type, debit_type, account_status, card_tier } = proxy.useDict('bank_card_type', 'debit_type', 'account_status', 'card_tier')
|
||||
const { limit_history_type, adjust_type } = proxy.useDict('limit_history_type', 'adjust_type')
|
||||
const { deal_type, deal_category } = proxy.useDict('deal_type', 'deal_category')
|
||||
const bankcardList = ref([])
|
||||
const open = ref(false)
|
||||
const loading = ref(true)
|
||||
@@ -245,6 +288,19 @@ const single = ref(true)
|
||||
const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const title = ref('')
|
||||
const titleDealRecord = ref('')
|
||||
const openDealRecord = ref(false)
|
||||
const currentAccountId = ref('')
|
||||
const loadingDealRecord = ref(false)
|
||||
const tableDealRecordData = ref([])
|
||||
const dealRecordTotal = ref(0)
|
||||
const queryDealRecordParams = ref({
|
||||
time: '',
|
||||
dealType: null,
|
||||
dealCategory: null,
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
|
||||
const detailOpen = ref(false)
|
||||
const openDetail = ref(false)
|
||||
@@ -256,6 +312,7 @@ const bankList = ref([])
|
||||
|
||||
const operateList = ref([
|
||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['invest:bankcard:query'] },
|
||||
{ id: 'dealRecord', icon: 'Tickets', title: '交易明细', hasPermi: ['invest:bankcard:query'] },
|
||||
{ id: 'limit', icon: 'Grid', title: '非柜面限额记录', hasPermi: ['invest:bankcard:edit'] },
|
||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['invest:bankcard:edit'] },
|
||||
{ id: 'delete', icon: 'Delete', title: '删除', hasPermi: ['invest:bankcard:remove'] }
|
||||
@@ -295,6 +352,9 @@ const data = reactive({
|
||||
|
||||
const handleOperate = (operate, row) => {
|
||||
switch (operate) {
|
||||
case 'dealRecord':
|
||||
handleDealRecord(row)
|
||||
break
|
||||
case 'limit':
|
||||
handleDetail(row)
|
||||
break
|
||||
@@ -429,6 +489,54 @@ function handleDetail(row) {
|
||||
limitHistoryList.value = response.rows
|
||||
})
|
||||
}
|
||||
|
||||
/** 交易明细按钮操作 */
|
||||
function handleDealRecord(row) {
|
||||
queryDealRecordParams.value.time = ''
|
||||
queryDealRecordParams.value.dealType = ''
|
||||
queryDealRecordParams.value.dealCategory = ''
|
||||
titleDealRecord.value = `${row.name}${row.code ? '【' + row.code + '】' : ''}交易明细`
|
||||
currentAccountId.value = row.id || ids.value
|
||||
getDealRecordList(1)
|
||||
openDealRecord.value = true
|
||||
}
|
||||
|
||||
const handleCurrentDealRecordChange = (num) => {
|
||||
getDealRecordList(num)
|
||||
}
|
||||
|
||||
const handleDealRecordQuery = () => {
|
||||
getDealRecordList(1)
|
||||
}
|
||||
|
||||
const resetDealRecordQuery = () => {
|
||||
proxy.resetForm('queryDealRecordRef')
|
||||
queryDealRecordParams.value.time = ''
|
||||
queryDealRecordParams.value.dealType = ''
|
||||
queryDealRecordParams.value.dealCategory = ''
|
||||
dealRecordTotal.value = 0
|
||||
getDealRecordList(1)
|
||||
}
|
||||
|
||||
const getDealRecordList = (num) => {
|
||||
loadingDealRecord.value = true
|
||||
const timeRange = queryDealRecordParams.value.time
|
||||
let st = ''
|
||||
let et = ''
|
||||
if (timeRange && timeRange.length === 2) {
|
||||
st = dayjs(timeRange[0]).format('YYYY-MM-DD')
|
||||
et = dayjs(timeRange[1]).format('YYYY-MM-DD')
|
||||
}
|
||||
queryDealRecordParams.value.startTime = st
|
||||
queryDealRecordParams.value.endTime = et
|
||||
queryDealRecordParams.value.pageNum = num
|
||||
queryDealRecordParams.value.accountId = currentAccountId.value
|
||||
listAccountDealRecord(queryDealRecordParams.value).then((response) => {
|
||||
loadingDealRecord.value = false
|
||||
tableDealRecordData.value = response.rows
|
||||
dealRecordTotal.value = response.total
|
||||
})
|
||||
}
|
||||
/** 查询储蓄账户信息列表 */
|
||||
function getList() {
|
||||
loading.value = true
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<el-table-column label="操作" width="210" align="center" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<div class="ctrl-btn d-flex">
|
||||
<el-tooltip v-for="item in operateList" :key="item.id" class="item" effect="dark" :content="item.title" placement="top">
|
||||
@@ -111,16 +111,58 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :title="titleDealRecord" v-model="openDealRecord" width="1200px" append-to-body destroy-on-close>
|
||||
<el-form :model="queryDealRecordParams" ref="queryDealRecordRef" inline>
|
||||
<el-form-item label="交易时间" prop="time">
|
||||
<el-date-picker v-model="queryDealRecordParams.time" type="daterange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" />
|
||||
</el-form-item>
|
||||
<el-form-item label="交易类型" style="width: 220px" prop="dealType">
|
||||
<el-select v-model="queryDealRecordParams.dealType" placeholder="请选择交易类型" clearable>
|
||||
<el-option v-for="dict in deal_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="交易类别" style="width: 220px" prop="dealCategory">
|
||||
<el-select v-model="queryDealRecordParams.dealCategory" placeholder="请选择交易类别" clearable>
|
||||
<el-option v-for="dict in deal_category" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="">
|
||||
<el-button type="primary" icon="Search" @click="handleDealRecordQuery">搜索</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="resetDealRecordQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loadingDealRecord" :data="tableDealRecordData">
|
||||
<el-table-column label="交易时间" align="center" prop="createTime" width="180" />
|
||||
<el-table-column label="交易金额" align="center" prop="amount" />
|
||||
<el-table-column label="当前余额" align="center" prop="currentBalance" />
|
||||
<el-table-column label="交易类型" align="center" width="120" prop="dealType">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="deal_type" :value="scope.row.dealType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易类别" align="center" width="150" prop="dealCategory">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="deal_category" :value="scope.row.dealCategory" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易子类别" align="center" width="150" prop="childCategoryName" />
|
||||
<el-table-column label="备注" align="center" width="300" prop="remark" />
|
||||
</el-table>
|
||||
<el-pagination small background layout="total, prev, pager, next" :total="dealRecordTotal" @current-change="handleCurrentDealRecordChange" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="FutureStocks">
|
||||
import { listFutureStocks, getFutureStocks, delFutureStocks, addFutureStocks, updateFutureStocks } from '@/api/invest/futureStocks'
|
||||
import { listAccountDealRecord } from '@/api/invest/accountDealRecord'
|
||||
import { listBankcardLend } from '@/api/invest/bankcardlend'
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { require } from '@/utils/require'
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { future_stock_type, account_status } = proxy.useDict('future_stock_type', 'account_status')
|
||||
const { deal_type, deal_category } = proxy.useDict('deal_type', 'deal_category')
|
||||
|
||||
const futureStocksList = ref([])
|
||||
const open = ref(false)
|
||||
@@ -131,9 +173,23 @@ const single = ref(true)
|
||||
const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const title = ref('')
|
||||
const titleDealRecord = ref('')
|
||||
const openDealRecord = ref(false)
|
||||
const currentAccountId = ref('')
|
||||
const loadingDealRecord = ref(false)
|
||||
const tableDealRecordData = ref([])
|
||||
const dealRecordTotal = ref(0)
|
||||
const queryDealRecordParams = ref({
|
||||
time: '',
|
||||
dealType: null,
|
||||
dealCategory: null,
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
const debitCardList = ref([])
|
||||
const operateList = ref([
|
||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['invest:futureStocks:query'] },
|
||||
{ id: 'dealRecord', icon: 'Tickets', title: '交易明细', hasPermi: ['invest:futureStocks:query'] },
|
||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['invest:futureStocks:edit'] },
|
||||
{ id: 'delete', icon: 'Delete', title: '删除', hasPermi: ['invest:futureStocks:remove'] }
|
||||
])
|
||||
@@ -172,6 +228,9 @@ const handleOperate = (operate, row) => {
|
||||
case 'view':
|
||||
handleView(row)
|
||||
break
|
||||
case 'dealRecord':
|
||||
handleDealRecord(row)
|
||||
break
|
||||
case 'edit':
|
||||
handleUpdate(row)
|
||||
break
|
||||
@@ -298,6 +357,54 @@ function handleUpdate(row) {
|
||||
})
|
||||
}
|
||||
|
||||
/** 交易明细按钮操作 */
|
||||
function handleDealRecord(row) {
|
||||
queryDealRecordParams.value.time = ''
|
||||
queryDealRecordParams.value.dealType = ''
|
||||
queryDealRecordParams.value.dealCategory = ''
|
||||
titleDealRecord.value = `${row.name}${row.code ? '【' + row.code + '】' : ''}交易明细`
|
||||
currentAccountId.value = row.id || ids.value
|
||||
getDealRecordList(1)
|
||||
openDealRecord.value = true
|
||||
}
|
||||
|
||||
const handleCurrentDealRecordChange = (num) => {
|
||||
getDealRecordList(num)
|
||||
}
|
||||
|
||||
const handleDealRecordQuery = () => {
|
||||
getDealRecordList(1)
|
||||
}
|
||||
|
||||
const resetDealRecordQuery = () => {
|
||||
proxy.resetForm('queryDealRecordRef')
|
||||
queryDealRecordParams.value.time = ''
|
||||
queryDealRecordParams.value.dealType = ''
|
||||
queryDealRecordParams.value.dealCategory = ''
|
||||
dealRecordTotal.value = 0
|
||||
getDealRecordList(1)
|
||||
}
|
||||
|
||||
const getDealRecordList = (num) => {
|
||||
loadingDealRecord.value = true
|
||||
const timeRange = queryDealRecordParams.value.time
|
||||
let st = ''
|
||||
let et = ''
|
||||
if (timeRange && timeRange.length > 0) {
|
||||
st = timeRange[0]
|
||||
et = timeRange[1]
|
||||
}
|
||||
queryDealRecordParams.value.startTime = st
|
||||
queryDealRecordParams.value.endTime = et
|
||||
queryDealRecordParams.value.pageNum = num
|
||||
queryDealRecordParams.value.accountId = currentAccountId.value
|
||||
listAccountDealRecord(queryDealRecordParams.value).then((response) => {
|
||||
loadingDealRecord.value = false
|
||||
tableDealRecordData.value = response.rows
|
||||
dealRecordTotal.value = response.total
|
||||
})
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs.futureStocksRef.validate((valid) => {
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
<el-table-column label="关联储蓄卡" align="center" prop="bankNameCode" />
|
||||
<el-table-column label="开户日期" align="center" prop="activationDate" width="120"> </el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<el-table-column label="操作" width="210" align="center" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<div class="ctrl-btn d-flex">
|
||||
<el-tooltip v-for="item in operateList" :key="item.id" class="item" effect="dark" :content="item.title" placement="top">
|
||||
@@ -121,16 +121,58 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :title="titleDealRecord" v-model="openDealRecord" width="1200px" append-to-body destroy-on-close>
|
||||
<el-form :model="queryDealRecordParams" ref="queryDealRecordRef" inline>
|
||||
<el-form-item label="交易时间" prop="time">
|
||||
<el-date-picker v-model="queryDealRecordParams.time" type="daterange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" />
|
||||
</el-form-item>
|
||||
<el-form-item label="交易类型" style="width: 220px" prop="dealType">
|
||||
<el-select v-model="queryDealRecordParams.dealType" placeholder="请选择交易类型" clearable>
|
||||
<el-option v-for="dict in deal_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="交易类别" style="width: 220px" prop="dealCategory">
|
||||
<el-select v-model="queryDealRecordParams.dealCategory" placeholder="请选择交易类别" clearable>
|
||||
<el-option v-for="dict in deal_category" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="">
|
||||
<el-button type="primary" icon="Search" @click="handleDealRecordQuery">搜索</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="resetDealRecordQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loadingDealRecord" :data="tableDealRecordData">
|
||||
<el-table-column label="交易时间" align="center" prop="createTime" width="180" />
|
||||
<el-table-column label="交易金额" align="center" prop="amount" />
|
||||
<el-table-column label="当前余额" align="center" prop="currentBalance" />
|
||||
<el-table-column label="交易类型" align="center" width="120" prop="dealType">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="deal_type" :value="scope.row.dealType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易类别" align="center" width="150" prop="dealCategory">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="deal_category" :value="scope.row.dealCategory" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易子类别" align="center" width="150" prop="childCategoryName" />
|
||||
<el-table-column label="备注" align="center" width="300" prop="remark" />
|
||||
</el-table>
|
||||
<el-pagination small background layout="total, prev, pager, next" :total="dealRecordTotal" @current-change="handleCurrentDealRecordChange" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="FutureStocks">
|
||||
import { listFutureStocks, getFutureStocks, delFutureStocks, addFutureStocks, updateFutureStocks } from '@/api/invest/futureStocks'
|
||||
import { listAccountDealRecord } from '@/api/invest/accountDealRecord'
|
||||
import { listBankcardLend } from '@/api/invest/bankcardlend'
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { require } from '@/utils/require'
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { future_stock_type, account_status } = proxy.useDict('future_stock_type', 'account_status')
|
||||
const { deal_type, deal_category } = proxy.useDict('deal_type', 'deal_category')
|
||||
|
||||
const futureStocksList = ref([])
|
||||
const open = ref(false)
|
||||
@@ -141,9 +183,23 @@ const single = ref(true)
|
||||
const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const title = ref('')
|
||||
const titleDealRecord = ref('')
|
||||
const openDealRecord = ref(false)
|
||||
const currentAccountId = ref('')
|
||||
const loadingDealRecord = ref(false)
|
||||
const tableDealRecordData = ref([])
|
||||
const dealRecordTotal = ref(0)
|
||||
const queryDealRecordParams = ref({
|
||||
time: '',
|
||||
dealType: null,
|
||||
dealCategory: null,
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
const debitCardList = ref([])
|
||||
const operateList = ref([
|
||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['invest:futureStocks:query'] },
|
||||
{ id: 'dealRecord', icon: 'Tickets', title: '交易明细', hasPermi: ['invest:futureStocks:query'] },
|
||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['invest:futureStocks:edit'] },
|
||||
{ id: 'delete', icon: 'Delete', title: '删除', hasPermi: ['invest:futureStocks:remove'] }
|
||||
])
|
||||
@@ -182,6 +238,9 @@ const handleOperate = (operate, row) => {
|
||||
case 'view':
|
||||
handleView(row)
|
||||
break
|
||||
case 'dealRecord':
|
||||
handleDealRecord(row)
|
||||
break
|
||||
case 'edit':
|
||||
handleUpdate(row)
|
||||
break
|
||||
@@ -308,6 +367,54 @@ function handleUpdate(row) {
|
||||
})
|
||||
}
|
||||
|
||||
/** 交易明细按钮操作 */
|
||||
function handleDealRecord(row) {
|
||||
queryDealRecordParams.value.time = ''
|
||||
queryDealRecordParams.value.dealType = ''
|
||||
queryDealRecordParams.value.dealCategory = ''
|
||||
titleDealRecord.value = `${row.name}${row.code ? '【' + row.code + '】' : ''}交易明细`
|
||||
currentAccountId.value = row.id || ids.value
|
||||
getDealRecordList(1)
|
||||
openDealRecord.value = true
|
||||
}
|
||||
|
||||
const handleCurrentDealRecordChange = (num) => {
|
||||
getDealRecordList(num)
|
||||
}
|
||||
|
||||
const handleDealRecordQuery = () => {
|
||||
getDealRecordList(1)
|
||||
}
|
||||
|
||||
const resetDealRecordQuery = () => {
|
||||
proxy.resetForm('queryDealRecordRef')
|
||||
queryDealRecordParams.value.time = ''
|
||||
queryDealRecordParams.value.dealType = ''
|
||||
queryDealRecordParams.value.dealCategory = ''
|
||||
dealRecordTotal.value = 0
|
||||
getDealRecordList(1)
|
||||
}
|
||||
|
||||
const getDealRecordList = (num) => {
|
||||
loadingDealRecord.value = true
|
||||
const timeRange = queryDealRecordParams.value.time
|
||||
let st = ''
|
||||
let et = ''
|
||||
if (timeRange && timeRange.length > 0) {
|
||||
st = timeRange[0]
|
||||
et = timeRange[1]
|
||||
}
|
||||
queryDealRecordParams.value.startTime = st
|
||||
queryDealRecordParams.value.endTime = et
|
||||
queryDealRecordParams.value.pageNum = num
|
||||
queryDealRecordParams.value.accountId = currentAccountId.value
|
||||
listAccountDealRecord(queryDealRecordParams.value).then((response) => {
|
||||
loadingDealRecord.value = false
|
||||
tableDealRecordData.value = response.rows
|
||||
dealRecordTotal.value = response.total
|
||||
})
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs.futureStocksRef.validate((valid) => {
|
||||
|
||||
@@ -128,16 +128,58 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :title="titleDealRecord" v-model="openDealRecord" width="1200px" append-to-body destroy-on-close>
|
||||
<el-form :model="queryDealRecordParams" ref="queryDealRecordRef" inline>
|
||||
<el-form-item label="交易时间" prop="time">
|
||||
<el-date-picker v-model="queryDealRecordParams.time" type="daterange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" />
|
||||
</el-form-item>
|
||||
<el-form-item label="交易类型" style="width: 220px" prop="dealType">
|
||||
<el-select v-model="queryDealRecordParams.dealType" placeholder="请选择交易类型" clearable>
|
||||
<el-option v-for="dict in deal_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="交易类别" style="width: 220px" prop="dealCategory">
|
||||
<el-select v-model="queryDealRecordParams.dealCategory" placeholder="请选择交易类别" clearable>
|
||||
<el-option v-for="dict in deal_category" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="">
|
||||
<el-button type="primary" icon="Search" @click="handleDealRecordQuery">搜索</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="resetDealRecordQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loadingDealRecord" :data="tableDealRecordData">
|
||||
<el-table-column label="交易时间" align="center" prop="createTime" width="180" />
|
||||
<el-table-column label="交易金额" align="center" prop="amount" />
|
||||
<el-table-column label="当前余额" align="center" prop="currentBalance" />
|
||||
<el-table-column label="交易类型" align="center" width="120" prop="dealType">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="deal_type" :value="scope.row.dealType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易类别" align="center" width="150" prop="dealCategory">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="deal_category" :value="scope.row.dealCategory" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易子类别" align="center" width="150" prop="childCategoryName" />
|
||||
<el-table-column label="备注" align="center" width="300" prop="remark" />
|
||||
</el-table>
|
||||
<el-pagination small background layout="total, prev, pager, next" :total="dealRecordTotal" @current-change="handleCurrentDealRecordChange" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="BankcardLend">
|
||||
import { listBankcardLend, getBankcardLend, delBankcardLend, addBankcardLend, updateBankcardLend } from '@/api/invest/bankcardlend'
|
||||
import { listAccountDealRecord } from '@/api/invest/accountDealRecord'
|
||||
import { listBankBaseInfo } from '@/api/invest/bankBaseInfo'
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { require } from '@/utils/require'
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { bank_card_type, lend_type, account_status } = proxy.useDict('bank_card_type', 'lend_type', 'account_status')
|
||||
const { deal_type, deal_category } = proxy.useDict('deal_type', 'deal_category')
|
||||
|
||||
const bankcardList = ref([])
|
||||
const open = ref(false)
|
||||
@@ -148,9 +190,23 @@ const single = ref(true)
|
||||
const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const title = ref('')
|
||||
const titleDealRecord = ref('')
|
||||
const openDealRecord = ref(false)
|
||||
const currentAccountId = ref('')
|
||||
const loadingDealRecord = ref(false)
|
||||
const tableDealRecordData = ref([])
|
||||
const dealRecordTotal = ref(0)
|
||||
const queryDealRecordParams = ref({
|
||||
time: '',
|
||||
dealType: null,
|
||||
dealCategory: null,
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
const bankList = ref([])
|
||||
const operateList = ref([
|
||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['invest:bankcard:query'] },
|
||||
{ id: 'dealRecord', icon: 'Tickets', title: '交易明细', hasPermi: ['invest:bankcard:query'] },
|
||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['invest:bankcard:edit'] },
|
||||
{ id: 'delete', icon: 'Delete', title: '删除', hasPermi: ['invest:bankcard:remove'] }
|
||||
])
|
||||
@@ -180,6 +236,9 @@ const handleOperate = (operate, row) => {
|
||||
case 'view':
|
||||
handleView(row)
|
||||
break
|
||||
case 'dealRecord':
|
||||
handleDealRecord(row)
|
||||
break
|
||||
case 'edit':
|
||||
handleUpdate(row)
|
||||
break
|
||||
@@ -312,6 +371,54 @@ function handleUpdate(row) {
|
||||
})
|
||||
}
|
||||
|
||||
/** 交易明细按钮操作 */
|
||||
function handleDealRecord(row) {
|
||||
queryDealRecordParams.value.time = ''
|
||||
queryDealRecordParams.value.dealType = ''
|
||||
queryDealRecordParams.value.dealCategory = ''
|
||||
titleDealRecord.value = `${row.name}${row.code ? '【' + row.code + '】' : ''}交易明细`
|
||||
currentAccountId.value = row.id || ids.value
|
||||
getDealRecordList(1)
|
||||
openDealRecord.value = true
|
||||
}
|
||||
|
||||
const handleCurrentDealRecordChange = (num) => {
|
||||
getDealRecordList(num)
|
||||
}
|
||||
|
||||
const handleDealRecordQuery = () => {
|
||||
getDealRecordList(1)
|
||||
}
|
||||
|
||||
const resetDealRecordQuery = () => {
|
||||
proxy.resetForm('queryDealRecordRef')
|
||||
queryDealRecordParams.value.time = ''
|
||||
queryDealRecordParams.value.dealType = ''
|
||||
queryDealRecordParams.value.dealCategory = ''
|
||||
dealRecordTotal.value = 0
|
||||
getDealRecordList(1)
|
||||
}
|
||||
|
||||
const getDealRecordList = (num) => {
|
||||
loadingDealRecord.value = true
|
||||
const timeRange = queryDealRecordParams.value.time
|
||||
let st = ''
|
||||
let et = ''
|
||||
if (timeRange && timeRange.length > 0) {
|
||||
st = timeRange[0]
|
||||
et = timeRange[1]
|
||||
}
|
||||
queryDealRecordParams.value.startTime = st
|
||||
queryDealRecordParams.value.endTime = et
|
||||
queryDealRecordParams.value.pageNum = num
|
||||
queryDealRecordParams.value.accountId = currentAccountId.value
|
||||
listAccountDealRecord(queryDealRecordParams.value).then((response) => {
|
||||
loadingDealRecord.value = false
|
||||
tableDealRecordData.value = response.rows
|
||||
dealRecordTotal.value = response.total
|
||||
})
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs.bankcardRef.validate((valid) => {
|
||||
|
||||
@@ -160,22 +160,34 @@
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.detailList && scope.row.detailList.length > 0">
|
||||
<div
|
||||
v-for="(item, index) in scope.row.detailList"
|
||||
:key="index"
|
||||
style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 6px 8px;
|
||||
margin-bottom: 4px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
"
|
||||
v-for="(group, groupIndex) in groupByRepaymentAccount(scope.row.detailList)"
|
||||
:key="groupIndex"
|
||||
style="padding: 6px 8px; margin-bottom: 4px; background: #f5f7fa; border-radius: 4px"
|
||||
>
|
||||
<span style="flex: 1"
|
||||
>{{ item.bankCardLendName }}(本金:{{ item.principal }}元,利息:{{ item.interest }}元,合计:{{ item.currentAmount }}元)</span
|
||||
<!-- 该账户下的所有明细 -->
|
||||
<div
|
||||
v-for="(item, itemIndex) in group.items"
|
||||
:key="itemIndex"
|
||||
:style="{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: '4px 0',
|
||||
borderBottom: itemIndex < group.items.length - 1 ? '1px dashed #dcdfe6' : 'none'
|
||||
}"
|
||||
>
|
||||
<el-button size="small" type="primary" icon="Coin" @click="handleRepayment(item)">还款</el-button>
|
||||
<span style="flex: 1"
|
||||
>{{ item.bankCardLendName }}(本金:{{ item.principal }}元,利息:{{ item.interest }}元,合计:{{ item.currentAmount }}元)</span
|
||||
>
|
||||
<el-button size="small" type="primary" icon="Coin" @click="handleRepayment(item)">还款</el-button>
|
||||
</div>
|
||||
<!-- 还款账户信息(同一账户只显示一次,放在明细后面) -->
|
||||
<div
|
||||
v-if="group.repaymentAccountName"
|
||||
style="margin-top: 4px; padding-top: 4px; color: #409eff; font-weight: 500; border-top: 1px solid #e4e7ed"
|
||||
>
|
||||
还款账户:{{ group.repaymentAccountName }}(余额:{{ group.repaymentAccountBalance }}元)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else v-html="formatMultiLineData(scope.row.detail)"></span>
|
||||
@@ -392,6 +404,26 @@ const reversedTableData = computed(() => {
|
||||
return []
|
||||
})
|
||||
|
||||
// 按还款账户分组处理明细数据
|
||||
const groupByRepaymentAccount = (detailList) => {
|
||||
if (!detailList || detailList.length === 0) return []
|
||||
|
||||
const groups = {}
|
||||
detailList.forEach((item) => {
|
||||
const key = item.repaymentAccountName || '未指定账户'
|
||||
if (!groups[key]) {
|
||||
groups[key] = {
|
||||
repaymentAccountName: item.repaymentAccountName,
|
||||
repaymentAccountBalance: item.repaymentAccountBalance,
|
||||
items: []
|
||||
}
|
||||
}
|
||||
groups[key].items.push(item)
|
||||
})
|
||||
|
||||
return Object.values(groups)
|
||||
}
|
||||
|
||||
function getList() {
|
||||
loading.value = true
|
||||
chartData.value = { name: [], value1: [] }
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<el-table-column label="供货商" align="center" width="100" prop="manufacture" />
|
||||
<!--
|
||||
<el-table-column label="商户码" align="center" prop="merchantCode" /> -->
|
||||
<el-table-column label="操作" align="center" width="150" class-name="small-padding fixed-width">
|
||||
<el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<div class="ctrl-btn d-flex">
|
||||
<el-tooltip v-for="item in operateList" :key="item.id" class="item" effect="dark" :content="item.title" placement="top">
|
||||
@@ -150,12 +150,44 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :title="titleSwipeRecord" v-model="openSwipeRecord" width="1200px" append-to-body destroy-on-close>
|
||||
<el-form :model="querySwipeRecordParams" ref="querySwipeRecordRef" inline>
|
||||
<el-form-item label="信用卡" style="width: 420px" prop="outAccountId">
|
||||
<el-select v-model="querySwipeRecordParams.outAccountId" placeholder="请选择信用卡" clearable>
|
||||
<el-option v-for="creditCard in creditCardList" :key="creditCard.id" :label="creditCard.nameCodeAvailableLimit" :value="creditCard.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="刷卡时间" prop="time">
|
||||
<el-date-picker v-model="querySwipeRecordParams.time" type="daterange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" />
|
||||
</el-form-item>
|
||||
<el-form-item label="">
|
||||
<el-button type="primary" icon="Search" @click="handleSwipeRecordQuery">搜索</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="resetSwipeRecordQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loadingSwipeRecord" :data="tableSwipeRecordData">
|
||||
<el-table-column type="index" label="序号" :index="swipeRecordIndexMethod" width="60" />
|
||||
<el-table-column label="POS机名称" align="center" prop="posName" />
|
||||
<el-table-column label="刷卡商户" align="center" prop="merchantName" />
|
||||
<el-table-column label="信用卡" align="center" prop="outAccountName" />
|
||||
<el-table-column label="刷卡时间" align="center" width="180" prop="createTime" />
|
||||
<el-table-column label="刷卡金额" align="center" prop="amount" />
|
||||
<el-table-column label="手续费" align="center" width="100" prop="commission" />
|
||||
<el-table-column label="储蓄卡" align="center" prop="inAccountName" />
|
||||
<el-table-column label="入账金额" align="center" width="120" prop="actualAmount" />
|
||||
</el-table>
|
||||
<el-pagination small background layout="total, prev, pager, next" :total="swipeRecordTotal" @current-change="handleCurrentSwipeRecordChange" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Posmachine">
|
||||
import { listPosmachine, getPosmachine, delPosmachine, addPosmachine, updatePosmachine } from '@/api/invest/posmachine'
|
||||
import { listBankcardLend } from '@/api/invest/bankcardlend'
|
||||
import { listAccounts } from '@/api/invest/accounts'
|
||||
import { listAccountsTransferRecord } from '@/api/invest/accountsTransferRecord'
|
||||
import dayjs from 'dayjs'
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { require } from '@/utils/require'
|
||||
const { proxy } = getCurrentInstance()
|
||||
@@ -171,8 +203,16 @@ const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const title = ref('')
|
||||
const debitCardList = ref([])
|
||||
const creditCardList = ref([])
|
||||
const titleSwipeRecord = ref('')
|
||||
const openSwipeRecord = ref(false)
|
||||
const currentPosId = ref('')
|
||||
const loadingSwipeRecord = ref(false)
|
||||
const tableSwipeRecordData = ref([])
|
||||
const swipeRecordTotal = ref(0)
|
||||
const operateList = ref([
|
||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['invest:posmachine:query'] },
|
||||
{ id: 'swipeRecord', icon: 'Tickets', title: '刷卡明细', hasPermi: ['invest:accountsTransferRecord:query'] },
|
||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['invest:posmachine:edit'] },
|
||||
{ id: 'delete', icon: 'Delete', title: '删除', hasPermi: ['invest:posmachine:remove'] }
|
||||
])
|
||||
@@ -193,6 +233,12 @@ const data = reactive({
|
||||
status: '1',
|
||||
pageSize: 1000
|
||||
},
|
||||
queryCreditCardParams: {
|
||||
pageNum: 1,
|
||||
type: '2',
|
||||
status: '1',
|
||||
pageSize: 1000
|
||||
},
|
||||
rules: {
|
||||
name: [{ required: true, message: 'pos机名称不能为空', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '账户状态不能为空', trigger: 'blur' }],
|
||||
@@ -208,12 +254,23 @@ const data = reactive({
|
||||
// merchantType: [{ required: true, message: '商户类型不能为空', trigger: 'change' }]
|
||||
}
|
||||
})
|
||||
const querySwipeRecordParams = ref({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
type: '1',
|
||||
time: '',
|
||||
posId: null,
|
||||
outAccountId: null
|
||||
})
|
||||
|
||||
const handleOperate = (operate, row) => {
|
||||
switch (operate) {
|
||||
case 'view':
|
||||
handleView(row)
|
||||
break
|
||||
case 'swipeRecord':
|
||||
handleSwipeRecord(row)
|
||||
break
|
||||
case 'edit':
|
||||
handleUpdate(row)
|
||||
break
|
||||
@@ -225,7 +282,7 @@ const handleOperate = (operate, row) => {
|
||||
}
|
||||
}
|
||||
|
||||
const { queryParams, queryDebitCardParams, form, rules } = toRefs(data)
|
||||
const { queryParams, queryDebitCardParams, queryCreditCardParams, form, rules } = toRefs(data)
|
||||
|
||||
/** 查询储蓄卡管理列表 */
|
||||
function getDebitCardList() {
|
||||
@@ -234,6 +291,13 @@ function getDebitCardList() {
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询信用卡管理列表 */
|
||||
function getCreditCardList() {
|
||||
listAccounts(queryCreditCardParams.value).then((response) => {
|
||||
creditCardList.value = response.rows
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询pos机信息列表 */
|
||||
function getList() {
|
||||
loading.value = true
|
||||
@@ -340,6 +404,59 @@ function handleUpdate(row) {
|
||||
})
|
||||
}
|
||||
|
||||
/** 刷卡明细按钮操作 */
|
||||
function handleSwipeRecord(row) {
|
||||
querySwipeRecordParams.value.time = ''
|
||||
querySwipeRecordParams.value.outAccountId = null
|
||||
titleSwipeRecord.value = `${row.name}${row.merchantName ? '【' + row.merchantName + '】' : ''}刷卡明细`
|
||||
currentPosId.value = row.id || ids.value
|
||||
getSwipeRecordList(1)
|
||||
openSwipeRecord.value = true
|
||||
}
|
||||
|
||||
const handleCurrentSwipeRecordChange = (num) => {
|
||||
getSwipeRecordList(num)
|
||||
}
|
||||
|
||||
const handleSwipeRecordQuery = () => {
|
||||
getSwipeRecordList(1)
|
||||
}
|
||||
|
||||
const resetSwipeRecordQuery = () => {
|
||||
proxy.resetForm('querySwipeRecordRef')
|
||||
querySwipeRecordParams.value.time = ''
|
||||
querySwipeRecordParams.value.outAccountId = null
|
||||
swipeRecordTotal.value = 0
|
||||
getSwipeRecordList(1)
|
||||
}
|
||||
|
||||
const getSwipeRecordList = (num) => {
|
||||
loadingSwipeRecord.value = true
|
||||
const timeRange = querySwipeRecordParams.value.time
|
||||
let st = ''
|
||||
let et = ''
|
||||
if (timeRange && timeRange.length === 2) {
|
||||
st = dayjs(timeRange[0]).format('YYYY-MM-DD')
|
||||
et = dayjs(timeRange[1]).format('YYYY-MM-DD')
|
||||
}
|
||||
querySwipeRecordParams.value.startTime = st
|
||||
querySwipeRecordParams.value.endTime = et
|
||||
querySwipeRecordParams.value.pageNum = num
|
||||
querySwipeRecordParams.value.posId = currentPosId.value
|
||||
querySwipeRecordParams.value.type = '1'
|
||||
listAccountsTransferRecord(querySwipeRecordParams.value).then((response) => {
|
||||
loadingSwipeRecord.value = false
|
||||
tableSwipeRecordData.value = response.rows
|
||||
swipeRecordTotal.value = response.total
|
||||
})
|
||||
}
|
||||
|
||||
const swipeRecordIndexMethod = (index) => {
|
||||
const nowPage = querySwipeRecordParams.value.pageNum
|
||||
const nowLimit = querySwipeRecordParams.value.pageSize
|
||||
return index + 1 + (nowPage - 1) * nowLimit
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs.posmachineRef.validate((valid) => {
|
||||
@@ -386,6 +503,7 @@ function handleExport() {
|
||||
`posmachine_${new Date().getTime()}.xlsx`
|
||||
)
|
||||
}
|
||||
getCreditCardList()
|
||||
getDebitCardList()
|
||||
getList()
|
||||
</script>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<el-table-column label="操作" width="210" align="center" class-name="small-padding fixed-width">
|
||||
<template v-slot="scope">
|
||||
<div class="ctrl-btn d-flex">
|
||||
<el-tooltip v-for="item in operateList" :key="item.id" class="item" effect="dark" :content="item.title" placement="top">
|
||||
@@ -111,16 +111,58 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :title="titleDealRecord" v-model="openDealRecord" width="1200px" append-to-body destroy-on-close>
|
||||
<el-form :model="queryDealRecordParams" ref="queryDealRecordRef" inline>
|
||||
<el-form-item label="交易时间" prop="time">
|
||||
<el-date-picker v-model="queryDealRecordParams.time" type="daterange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" />
|
||||
</el-form-item>
|
||||
<el-form-item label="交易类型" style="width: 220px" prop="dealType">
|
||||
<el-select v-model="queryDealRecordParams.dealType" placeholder="请选择交易类型" clearable>
|
||||
<el-option v-for="dict in deal_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="交易类别" style="width: 220px" prop="dealCategory">
|
||||
<el-select v-model="queryDealRecordParams.dealCategory" placeholder="请选择交易类别" clearable>
|
||||
<el-option v-for="dict in deal_category" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="">
|
||||
<el-button type="primary" icon="Search" @click="handleDealRecordQuery">搜索</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="resetDealRecordQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loadingDealRecord" :data="tableDealRecordData">
|
||||
<el-table-column label="交易时间" align="center" prop="createTime" width="180" />
|
||||
<el-table-column label="交易金额" align="center" prop="amount" />
|
||||
<el-table-column label="当前余额" align="center" prop="currentBalance" />
|
||||
<el-table-column label="交易类型" align="center" width="120" prop="dealType">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="deal_type" :value="scope.row.dealType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易类别" align="center" width="150" prop="dealCategory">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="deal_category" :value="scope.row.dealCategory" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易子类别" align="center" width="150" prop="childCategoryName" />
|
||||
<el-table-column label="备注" align="center" width="300" prop="remark" />
|
||||
</el-table>
|
||||
<el-pagination small background layout="total, prev, pager, next" :total="dealRecordTotal" @current-change="handleCurrentDealRecordChange" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="FutureStocks">
|
||||
import { listFutureStocks, getFutureStocks, delFutureStocks, addFutureStocks, updateFutureStocks } from '@/api/invest/futureStocks'
|
||||
import { listAccountDealRecord } from '@/api/invest/accountDealRecord'
|
||||
import { listBankcardLend } from '@/api/invest/bankcardlend'
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { require } from '@/utils/require'
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { future_stock_type, account_status } = proxy.useDict('future_stock_type', 'account_status')
|
||||
const { deal_type, deal_category } = proxy.useDict('deal_type', 'deal_category')
|
||||
|
||||
const futureStocksList = ref([])
|
||||
const open = ref(false)
|
||||
@@ -131,9 +173,23 @@ const single = ref(true)
|
||||
const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const title = ref('')
|
||||
const titleDealRecord = ref('')
|
||||
const openDealRecord = ref(false)
|
||||
const currentAccountId = ref('')
|
||||
const loadingDealRecord = ref(false)
|
||||
const tableDealRecordData = ref([])
|
||||
const dealRecordTotal = ref(0)
|
||||
const queryDealRecordParams = ref({
|
||||
time: '',
|
||||
dealType: null,
|
||||
dealCategory: null,
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
const debitCardList = ref([])
|
||||
const operateList = ref([
|
||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['invest:futureStocks:query'] },
|
||||
{ id: 'dealRecord', icon: 'Tickets', title: '交易明细', hasPermi: ['invest:futureStocks:query'] },
|
||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['invest:futureStocks:edit'] },
|
||||
{ id: 'delete', icon: 'Delete', title: '删除', hasPermi: ['invest:futureStocks:remove'] }
|
||||
])
|
||||
@@ -172,6 +228,9 @@ const handleOperate = (operate, row) => {
|
||||
case 'view':
|
||||
handleView(row)
|
||||
break
|
||||
case 'dealRecord':
|
||||
handleDealRecord(row)
|
||||
break
|
||||
case 'edit':
|
||||
handleUpdate(row)
|
||||
break
|
||||
@@ -298,6 +357,54 @@ function handleUpdate(row) {
|
||||
})
|
||||
}
|
||||
|
||||
/** 交易明细按钮操作 */
|
||||
function handleDealRecord(row) {
|
||||
queryDealRecordParams.value.time = ''
|
||||
queryDealRecordParams.value.dealType = ''
|
||||
queryDealRecordParams.value.dealCategory = ''
|
||||
titleDealRecord.value = `${row.name}${row.code ? '【' + row.code + '】' : ''}交易明细`
|
||||
currentAccountId.value = row.id || ids.value
|
||||
getDealRecordList(1)
|
||||
openDealRecord.value = true
|
||||
}
|
||||
|
||||
const handleCurrentDealRecordChange = (num) => {
|
||||
getDealRecordList(num)
|
||||
}
|
||||
|
||||
const handleDealRecordQuery = () => {
|
||||
getDealRecordList(1)
|
||||
}
|
||||
|
||||
const resetDealRecordQuery = () => {
|
||||
proxy.resetForm('queryDealRecordRef')
|
||||
queryDealRecordParams.value.time = ''
|
||||
queryDealRecordParams.value.dealType = ''
|
||||
queryDealRecordParams.value.dealCategory = ''
|
||||
dealRecordTotal.value = 0
|
||||
getDealRecordList(1)
|
||||
}
|
||||
|
||||
const getDealRecordList = (num) => {
|
||||
loadingDealRecord.value = true
|
||||
const timeRange = queryDealRecordParams.value.time
|
||||
let st = ''
|
||||
let et = ''
|
||||
if (timeRange && timeRange.length > 0) {
|
||||
st = timeRange[0]
|
||||
et = timeRange[1]
|
||||
}
|
||||
queryDealRecordParams.value.startTime = st
|
||||
queryDealRecordParams.value.endTime = et
|
||||
queryDealRecordParams.value.pageNum = num
|
||||
queryDealRecordParams.value.accountId = currentAccountId.value
|
||||
listAccountDealRecord(queryDealRecordParams.value).then((response) => {
|
||||
loadingDealRecord.value = false
|
||||
tableDealRecordData.value = response.rows
|
||||
dealRecordTotal.value = response.total
|
||||
})
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs.futureStocksRef.validate((valid) => {
|
||||
|
||||
1858
src/views/invest/transactionAnalysisScreen/index.vue
Normal file
1858
src/views/invest/transactionAnalysisScreen/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -67,6 +67,7 @@
|
||||
<script setup>
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { getCodeImg, register } from '@/api/login'
|
||||
import { passwordValidator } from '@/utils/validate'
|
||||
|
||||
const router = useRouter()
|
||||
const { proxy } = getCurrentInstance()
|
||||
@@ -95,7 +96,7 @@ const registerRules = {
|
||||
],
|
||||
password: [
|
||||
{ required: true, trigger: 'blur', message: '请输入您的密码' },
|
||||
{ min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' }
|
||||
{ validator: passwordValidator, trigger: 'blur' }
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, trigger: 'blur', message: '请再次输入您的密码' },
|
||||
@@ -127,7 +128,7 @@ function handleRegister() {
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false
|
||||
if (captchaEnabled) {
|
||||
if (captchaEnabled.value) {
|
||||
getCode()
|
||||
}
|
||||
})
|
||||
|
||||
379
src/views/system/feedback/index.vue
Normal file
379
src/views/system/feedback/index.vue
Normal file
@@ -0,0 +1,379 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="search-con" ref="searchHeightRef">
|
||||
<div class="title">查询条件</div>
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px">
|
||||
<el-form-item label="反馈类型" prop="feedbackType">
|
||||
<el-select v-model="queryParams.feedbackType" placeholder="请选择反馈类型" clearable style="width: 200px">
|
||||
<el-option v-for="dict in feedbackTypeOptions" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="处理状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择处理状态" clearable style="width: 200px">
|
||||
<el-option v-for="dict in feedbackStatusOptions" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="反馈来源" prop="source">
|
||||
<el-select v-model="queryParams.source" placeholder="请选择反馈来源" clearable style="width: 200px">
|
||||
<el-option v-for="item in sourceOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交用户" prop="createBy">
|
||||
<el-input v-model="queryParams.createBy" placeholder="请输入提交用户" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="search-btn-con">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main-con" :style="{ height: mainStyleHeight }">
|
||||
<div class="title-con">
|
||||
<div class="title">反馈列表</div>
|
||||
<div class="operate-btn-con">
|
||||
<el-button icon="Check" :disabled="single" @click="handleProcess" v-hasPermi="['system:feedback:edit']">标记已处理</el-button>
|
||||
<el-button icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:feedback:remove']">删除</el-button>
|
||||
<el-button icon="Download" @click="handleExport" v-hasPermi="['system:feedback:export']">导出</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-con" v-loading="loading">
|
||||
<el-table :data="feedbackList" @selection-change="handleSelectionChange" stripe height="calc(100% - 0.62rem)">
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column type="index" label="序号" :index="indexMethod" width="60" align="center" />
|
||||
<el-table-column label="反馈类型" align="center" prop="feedbackType" width="110" />
|
||||
<el-table-column label="反馈内容" prop="content" min-width="240" :show-overflow-tooltip="true">
|
||||
<template #default="scope">
|
||||
<span class="content-text">{{ scope.row.content }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="联系方式" align="center" prop="contact" width="140" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="截图" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
v-if="getImageList(scope.row).length"
|
||||
class="feedback-thumb"
|
||||
:src="getImageList(scope.row)[0]"
|
||||
:preview-src-list="getImageList(scope.row)"
|
||||
fit="cover"
|
||||
/>
|
||||
<span v-else class="empty-text">无</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="来源" align="center" prop="source" width="90" />
|
||||
<el-table-column label="状态" align="center" prop="status" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="statusTagType(scope.row.status)">{{ statusText(scope.row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提交用户" align="center" prop="createBy" width="120" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="提交时间" align="center" prop="createTime" width="160">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" width="180" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="详情" placement="top">
|
||||
<el-button circle icon="View" @click="handleView(scope.row)" v-hasPermi="['system:feedback:query']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="回复" placement="top">
|
||||
<el-button circle icon="ChatDotRound" @click="handleReply(scope.row)" v-hasPermi="['system:feedback:reply']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button circle icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:feedback:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
small
|
||||
background
|
||||
layout="total, prev, pager, next"
|
||||
:total="total"
|
||||
v-model:current-page="currentPage"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog title="反馈详情" v-model="detailOpen" width="760px" append-to-body>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="反馈类型">{{ detailForm.feedbackType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="处理状态">
|
||||
<el-tag :type="statusTagType(detailForm.status)">{{ statusText(detailForm.status) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="联系方式">{{ detailForm.contact || '未填写' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="反馈来源">{{ detailForm.source || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="提交用户">{{ detailForm.createBy || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="提交时间">{{ parseTime(detailForm.createTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="反馈内容" :span="2">
|
||||
<div class="detail-content">{{ detailForm.content }}</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="截图" :span="2">
|
||||
<div v-if="getImageList(detailForm).length" class="image-list">
|
||||
<el-image
|
||||
v-for="image in getImageList(detailForm)"
|
||||
:key="image"
|
||||
class="detail-image"
|
||||
:src="image"
|
||||
:preview-src-list="getImageList(detailForm)"
|
||||
fit="cover"
|
||||
/>
|
||||
</div>
|
||||
<span v-else>无</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="回复内容" :span="2">{{ detailForm.replyContent || '暂无回复' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="回复人">{{ detailForm.replyBy || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="回复时间">{{ parseTime(detailForm.replyTime) || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2">{{ detailForm.remark || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleReply(detailForm)" v-hasPermi="['system:feedback:reply']">回复</el-button>
|
||||
<el-button @click="detailOpen = false">关 闭</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="回复反馈" v-model="replyOpen" width="600px" append-to-body>
|
||||
<el-form ref="replyRef" :model="replyForm" :rules="replyRules" label-width="80px">
|
||||
<el-form-item label="回复内容" prop="replyContent">
|
||||
<el-input v-model="replyForm.replyContent" type="textarea" :rows="5" maxlength="1000" show-word-limit placeholder="请输入回复内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitReply">确 定</el-button>
|
||||
<el-button @click="replyOpen = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Feedback">
|
||||
import { listFeedback, getFeedback, updateFeedback, replyFeedback, delFeedback } from '@/api/system/feedback'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { feedback_type, feedback_status } = proxy.useDict('feedback_type', 'feedback_status')
|
||||
|
||||
const defaultFeedbackTypeOptions = [
|
||||
{ label: '功能建议', value: '功能建议' },
|
||||
{ label: '问题反馈', value: '问题反馈' },
|
||||
{ label: '数据异常', value: '数据异常' },
|
||||
{ label: '其他', value: '其他' }
|
||||
]
|
||||
const defaultFeedbackStatusOptions = [
|
||||
{ label: '待处理', value: '0' },
|
||||
{ label: '已处理', value: '1' },
|
||||
{ label: '已回复', value: '2' }
|
||||
]
|
||||
const sourceOptions = [
|
||||
{ label: '小程序', value: '小程序' },
|
||||
{ label: 'H5', value: 'H5' },
|
||||
{ label: 'App', value: 'App' }
|
||||
]
|
||||
|
||||
const feedbackTypeOptions = computed(() => (feedback_type.value && feedback_type.value.length ? feedback_type.value : defaultFeedbackTypeOptions))
|
||||
const feedbackStatusOptions = computed(() => (feedback_status.value && feedback_status.value.length ? feedback_status.value : defaultFeedbackStatusOptions))
|
||||
|
||||
const feedbackList = ref([])
|
||||
const loading = ref(true)
|
||||
const showSearch = ref(true)
|
||||
const ids = ref([])
|
||||
const single = ref(true)
|
||||
const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const currentPage = ref(1)
|
||||
const mainStyleHeight = ref(0)
|
||||
const searchHeightRef = ref(null)
|
||||
const detailOpen = ref(false)
|
||||
const replyOpen = ref(false)
|
||||
const detailForm = ref({})
|
||||
const replyForm = ref({})
|
||||
|
||||
const data = reactive({
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
feedbackType: undefined,
|
||||
status: undefined,
|
||||
source: undefined,
|
||||
createBy: undefined
|
||||
},
|
||||
replyRules: {
|
||||
replyContent: [{ required: true, message: '回复内容不能为空', trigger: 'blur' }]
|
||||
}
|
||||
})
|
||||
const { queryParams, replyRules } = toRefs(data)
|
||||
|
||||
function getList() {
|
||||
loading.value = true
|
||||
listFeedback(queryParams.value).then((response) => {
|
||||
feedbackList.value = response.rows
|
||||
total.value = response.total
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1
|
||||
currentPage.value = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
function resetQuery() {
|
||||
proxy.resetForm('queryRef')
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map((item) => item.id)
|
||||
single.value = selection.length !== 1
|
||||
multiple.value = !selection.length
|
||||
}
|
||||
|
||||
function handleView(row) {
|
||||
getFeedback(row.id).then((response) => {
|
||||
detailForm.value = response.data || {}
|
||||
detailOpen.value = true
|
||||
})
|
||||
}
|
||||
|
||||
function handleReply(row) {
|
||||
const id = row.id
|
||||
getFeedback(id).then((response) => {
|
||||
const data = response.data || row
|
||||
replyForm.value = {
|
||||
id: data.id,
|
||||
replyContent: data.replyContent || ''
|
||||
}
|
||||
replyOpen.value = true
|
||||
})
|
||||
}
|
||||
|
||||
function submitReply() {
|
||||
proxy.$refs.replyRef.validate((valid) => {
|
||||
if (valid) {
|
||||
replyFeedback(replyForm.value).then(() => {
|
||||
proxy.$modal.msgSuccess('回复成功')
|
||||
replyOpen.value = false
|
||||
detailOpen.value = false
|
||||
getList()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function handleProcess(row) {
|
||||
const id = row.id || ids.value[0]
|
||||
if (!id) return
|
||||
proxy.$modal
|
||||
.confirm('是否确认将该反馈标记为已处理?')
|
||||
.then(() => updateFeedback({ id, status: '1' }))
|
||||
.then(() => {
|
||||
proxy.$modal.msgSuccess('操作成功')
|
||||
getList()
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
function handleDelete(row) {
|
||||
const feedbackIds = row.id || ids.value
|
||||
proxy.$modal
|
||||
.confirm('是否确认删除意见反馈编号为"' + feedbackIds + '"的数据项?')
|
||||
.then(() => delFeedback(feedbackIds))
|
||||
.then(() => {
|
||||
getList()
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
function handleExport() {
|
||||
proxy.download(
|
||||
'system/feedback/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`feedback_${new Date().getTime()}.xlsx`
|
||||
)
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
queryParams.value.pageNum = val
|
||||
getList()
|
||||
}
|
||||
|
||||
const indexMethod = (index) => {
|
||||
const nowPage = queryParams.value.pageNum
|
||||
const nowLimit = queryParams.value.pageSize
|
||||
return index + 1 + (nowPage - 1) * nowLimit
|
||||
}
|
||||
|
||||
function getImageList(row) {
|
||||
if (!row) return []
|
||||
if (Array.isArray(row.imageList) && row.imageList.length) {
|
||||
return row.imageList.filter(Boolean)
|
||||
}
|
||||
if (row.images) {
|
||||
return String(row.images)
|
||||
.split(',')
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
function statusText(status) {
|
||||
const text = proxy.selectDictLabel(feedbackStatusOptions.value, status)
|
||||
return text || '-'
|
||||
}
|
||||
|
||||
function statusTagType(status) {
|
||||
const map = {
|
||||
0: 'warning',
|
||||
1: 'info',
|
||||
2: 'success'
|
||||
}
|
||||
return map[status] || 'info'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (searchHeightRef.value) {
|
||||
mainStyleHeight.value = `calc(100% - ${(searchHeightRef.value.clientHeight + 10) / 100}rem)`
|
||||
}
|
||||
})
|
||||
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.content-text {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
}
|
||||
.empty-text {
|
||||
color: #909399;
|
||||
}
|
||||
.feedback-thumb {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.detail-content {
|
||||
line-height: 24px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.image-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
.detail-image {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -238,6 +238,7 @@
|
||||
<script setup name="User">
|
||||
import { getToken } from '@/utils/auth'
|
||||
import { changeUserStatus, listUser, resetUserPwd, delUser, getUser, updateUser, addUser, deptTreeSelect } from '@/api/system/user'
|
||||
import { passwordValidator } from '@/utils/validate'
|
||||
import { onMounted } from 'vue'
|
||||
const router = useRouter()
|
||||
const { proxy } = getCurrentInstance()
|
||||
@@ -304,7 +305,7 @@ const data = reactive({
|
||||
nickName: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
|
||||
password: [
|
||||
{ required: true, message: '用户密码不能为空', trigger: 'blur' },
|
||||
{ min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' }
|
||||
{ validator: passwordValidator, trigger: 'blur' }
|
||||
],
|
||||
email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }],
|
||||
phonenumber: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }]
|
||||
@@ -418,8 +419,8 @@ function handleResetPwd(row) {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
closeOnClickModal: false,
|
||||
inputPattern: /^.{5,20}$/,
|
||||
inputErrorMessage: '用户密码长度必须介于 5 和 20 之间'
|
||||
inputPattern: /^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[^A-Za-z0-9\s])\S{8,20}$/,
|
||||
inputErrorMessage: '密码需 8-20 位,且须同时含大小写字母、数字和特殊字符(不能含空格)'
|
||||
})
|
||||
.then(({ value }) => {
|
||||
resetUserPwd(row.userId, value).then((response) => {
|
||||
@@ -507,7 +508,7 @@ function handleUpdate(row) {
|
||||
form.value.roleIds = response.roleIds
|
||||
open.value = true
|
||||
title.value = '修改用户'
|
||||
form.password = ''
|
||||
form.value.password = ''
|
||||
})
|
||||
}
|
||||
/** 提交按钮 */
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
<script setup>
|
||||
import { updateUserPwd } from '@/api/system/user'
|
||||
import { passwordValidator } from '@/utils/validate'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
@@ -38,7 +39,7 @@ const rules = ref({
|
||||
oldPassword: [{ required: true, message: '旧密码不能为空', trigger: 'blur' }],
|
||||
newPassword: [
|
||||
{ required: true, message: '新密码不能为空', trigger: 'blur' },
|
||||
{ min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
|
||||
{ validator: passwordValidator, trigger: 'blur' }
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: '确认密码不能为空', trigger: 'blur' },
|
||||
|
||||
@@ -42,8 +42,8 @@ export default defineConfig(({ mode, command }) => {
|
||||
proxy: {
|
||||
// https://cn.vitejs.dev/config/#server-proxy
|
||||
'/dev-api': {
|
||||
target: 'https://www.qdintc.com/prod-api/',
|
||||
// target: 'http://127.0.0.1:8080',
|
||||
// target: 'https://www.qdintc.com/prod-api/',
|
||||
target: 'http://127.0.0.1:8080',
|
||||
changeOrigin: true,
|
||||
rewrite: (p) => p.replace(/^\/dev-api/, '')
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user