BUG修复,及部分页面增加按钮权限

This commit is contained in:
Songsl 2026-04-28 14:31:56 +08:00
parent adcbbb6752
commit 2f54ebef78
17 changed files with 1284 additions and 980 deletions

View File

@ -416,6 +416,7 @@ const handleSubmit = async () => {
try { try {
await editCustomerApi({ ...params, point_diffs_json: JSON.stringify(marginRatioJSON) }) await editCustomerApi({ ...params, point_diffs_json: JSON.stringify(marginRatioJSON) })
dialogVisible.value = false dialogVisible.value = false
await getCustomerList(userId.value)
} catch (err) { } catch (err) {
console.log('提交失败:', err) console.log('提交失败:', err)
} }
@ -428,6 +429,7 @@ const handleSubmit = async () => {
fee_handling_percent: commissionRatioForm.value.commissionRatio, fee_handling_percent: commissionRatioForm.value.commissionRatio,
}) })
dialogVisible.value = false dialogVisible.value = false
await getCustomerList(userId.value)
} catch (err) { } catch (err) {
console.log('提交失败:', err) console.log('提交失败:', err)
} }

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="deposit-channel"> <div class="deposit-channel">
<div class="bar"> <div class="bar">
<el-button type="primary" @click="handleAdd">添加</el-button> <el-button type="primary" @click="handleAdd" v-auth="'channel_createChannel'">添加</el-button>
<el-button type="primary" @click="handleEdit">编辑</el-button> <el-button type="primary" @click="handleEdit" v-auth="'channel_editChannel'">编辑</el-button>
<el-button type="danger" @click="handleDelete">删除</el-button> <el-button type="danger" @click="handleDelete" v-auth="'channel_delChannel'">删除</el-button>
</div> </div>
<DepositChannelTable <DepositChannelTable
ref="tableRef" ref="tableRef"

View File

@ -9,8 +9,12 @@
> >
<template #button> <template #button>
<!-- 审核动作先保留按钮交互后续可直接接接口 --> <!-- 审核动作先保留按钮交互后续可直接接接口 -->
<el-button type="danger" plain @click="handleReject">驳回</el-button> <el-button type="danger" plain @click="handleReject" v-auth="'user_reviewRechargeOrder'"
<el-button type="success" @click="handleApprove">通过</el-button> >驳回</el-button
>
<el-button type="success" @click="handleApprove" v-auth="'user_reviewRechargeOrder'"
>通过</el-button
>
<el-button type="primary" plain @click="handleExport">导出</el-button> <el-button type="primary" plain @click="handleExport">导出</el-button>
</template> </template>
</RechangeSearch> </RechangeSearch>

View File

@ -1,77 +1,125 @@
<template> <template>
<div class="transfer"> <div class="transfer">
<div class="title">转账</div> <div class="title">转账</div>
<el-card class="transfer-card"> <el-card class="transfer-card">
<div class="transfer-item">1.佣金钱包转出时,需要管理员审核通过后才能转账成功其余内部转账操作无需通过管理员审核如发现对应账号没有收到转账资金请于客服人员联系</div> <div class="transfer-item">
<div class="transfer-item">2.如转出账号与转入账号的货币单位不同系统将按照当日汇率进行换算</div> 1.佣金钱包转出时,需要管理员审核通过后才能转账成功其余内部转账操作无需通过管理员审核如发现对应账号没有收到转账资金请于客服人员联系
<div class="transfer-item">3.若有在途订单时进行了MT账号的转出操作因浮动盈亏影响可能会导致转出失败</div> </div>
<div class="transfer-item">4.第三方转账只支持钱包之间转账且会转到对方主钱包(佣金钱包不允许转入)需要管理员审核通过后才能转账成功</div> <div class="transfer-item">
</el-card> 2.如转出账号与转入账号的货币单位不同系统将按照当日汇率进行换算
<div class="transfer-type"> </div>
<div class="type-title">转账类型</div> <div class="transfer-item">
<div class="tab"> 3.若有在途订单时进行了MT账号的转出操作因浮动盈亏影响可能会导致转出失败
<el-button :type="transferType === 'internal' ? 'primary' : 'default'" </div>
@click="handleTransferTabClick('internal')">内部转账</el-button> <div class="transfer-item">
<el-button :type="transferType === 'external' ? 'primary' : 'default'" 4.第三方转账只支持钱包之间转账且会转到对方主钱包(佣金钱包不允许转入)需要管理员审核通过后才能转账成功
@click="handleTransferTabClick('external')">第三方转账</el-button> </div>
</div> </el-card>
</div> <div class="transfer-type">
<div class="transfer-form"> <div class="type-title">转账类型</div>
<div class="external-item" v-if="transferType === 'external'"> <div class="tab">
<div class="externa-title">转出账号: 主钱包</div> <el-button
<el-card class="external-card"> :type="transferType === 'internal' ? 'primary' : 'default'"
<el-icon> @click="handleTransferTabClick('internal')"
<svg t="1774503695687" class="icon" viewBox="0 0 1024 1024" version="1.1" >内部转账</el-button
xmlns="http://www.w3.org/2000/svg" p-id="2732" width="200" height="200"> >
<path <el-button
d="M512.54 156.23a353.21 353.21 0 1 1-138 27.83 352.25 352.25 0 0 1 138-27.83m0-60C283.61 96.23 98 281.81 98 510.74s185.61 414.51 414.54 414.51 414.51-185.58 414.51-414.51S741.47 96.23 512.54 96.23z" :type="transferType === 'external' ? 'primary' : 'default'"
p-id="2733"></path> @click="handleTransferTabClick('external')"
<path >第三方转账</el-button
d="M512 478a29.89 29.89 0 0 1-21.13-8.71l-133-132a30 30 0 1 1 42.26-42.58l133 132A30 30 0 0 1 512 478z" >
p-id="2734"></path> </div>
<path
d="M512 478a30 30 0 0 1-21.21-51.21l128-128a30 30 0 0 1 42.42 42.42l-128 128A29.88 29.88 0 0 1 512 478z"
p-id="2735"></path>
<path
d="M660 529H366a30 30 0 0 1 0-60h294a30 30 0 0 1 0 60z m0 95H366a30 30 0 0 1 0-60h294a30 30 0 0 1 0 60z"
p-id="2736"></path>
<path d="M512 776a30 30 0 0 1-30-30V448a30 30 0 0 1 60 0v298a30 30 0 0 1-30 30z"
p-id="2737"></path>
</svg>
</el-icon>
<div class="money">
<span>USD</span>
<span>{{ transferForm.MaxOutAmount }}</span>
</div>
</el-card>
</div>
<TransferForm :key="transferType" class="transfer-main-form" :form-data="transferForm" :form-rules="transferFormRules"
:form-items="transferFormItemOptions" :options-map="transferFormOptionsMap" ref="mainTransferFormRef">
</TransferForm>
<div class="transfer-type verify" v-if="transferType === 'external'">
<div class="type-title">验证方式</div>
<div class="tab">
<el-button :type="verifyType === 'email' ? 'primary' : 'default'"
@click="handleVerifyTabClick('email')">邮箱验证</el-button>
<el-button :type="verifyType === 'phone' ? 'primary' : 'default'"
@click="handleVerifyTabClick('phone')">手机号验证</el-button>
</div>
<div class="username">{{ verifyType === "email" ? userInfo.email : userInfo.phone }}</div>
<el-form class="verify-form" :model="transferForm" :rules="transferFormRules" ref="verifyFormRef">
<el-form-item prop="code">
<el-input v-model="transferForm.code" style="max-width: 300px" placeholder="请输入验证码">
</el-input>
<el-button type="primary" @click="handleSendCode" :disabled="sendState.isCounting">{{
sendState.isCounting ? `${sendState.countdown}s后重新获取` : '发送验证码' }}</el-button>
</el-form-item>
</el-form>
</div>
<el-button type="primary" @click="handleSubmit">提交转账</el-button>
</div>
</div> </div>
<div class="transfer-form">
<div class="external-item" v-if="transferType === 'external'">
<div class="externa-title">转出账号: 主钱包</div>
<el-card class="external-card">
<el-icon>
<svg
t="1774503695687"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="2732"
width="200"
height="200"
>
<path
d="M512.54 156.23a353.21 353.21 0 1 1-138 27.83 352.25 352.25 0 0 1 138-27.83m0-60C283.61 96.23 98 281.81 98 510.74s185.61 414.51 414.54 414.51 414.51-185.58 414.51-414.51S741.47 96.23 512.54 96.23z"
p-id="2733"
></path>
<path
d="M512 478a29.89 29.89 0 0 1-21.13-8.71l-133-132a30 30 0 1 1 42.26-42.58l133 132A30 30 0 0 1 512 478z"
p-id="2734"
></path>
<path
d="M512 478a30 30 0 0 1-21.21-51.21l128-128a30 30 0 0 1 42.42 42.42l-128 128A29.88 29.88 0 0 1 512 478z"
p-id="2735"
></path>
<path
d="M660 529H366a30 30 0 0 1 0-60h294a30 30 0 0 1 0 60z m0 95H366a30 30 0 0 1 0-60h294a30 30 0 0 1 0 60z"
p-id="2736"
></path>
<path
d="M512 776a30 30 0 0 1-30-30V448a30 30 0 0 1 60 0v298a30 30 0 0 1-30 30z"
p-id="2737"
></path>
</svg>
</el-icon>
<div class="money">
<span>USD</span>
<span>{{ transferForm.MaxOutAmount }}</span>
</div>
</el-card>
</div>
<TransferForm
:key="transferType"
class="transfer-main-form"
:form-data="transferForm"
:form-rules="transferFormRules"
:form-items="transferFormItemOptions"
:options-map="transferFormOptionsMap"
ref="mainTransferFormRef"
>
</TransferForm>
<div class="transfer-type verify" v-if="transferType === 'external'">
<div class="type-title">验证方式</div>
<div class="tab">
<el-button
:type="verifyType === 'email' ? 'primary' : 'default'"
@click="handleVerifyTabClick('email')"
>邮箱验证</el-button
>
<el-button
:type="verifyType === 'phone' ? 'primary' : 'default'"
@click="handleVerifyTabClick('phone')"
>手机号验证</el-button
>
</div>
<div class="username">{{ verifyType === 'email' ? userInfo.email : userInfo.phone }}</div>
<el-form
class="verify-form"
:model="transferForm"
:rules="transferFormRules"
ref="verifyFormRef"
>
<el-form-item prop="code">
<el-input
v-model="transferForm.code"
style="max-width: 300px"
placeholder="请输入验证码"
>
</el-input>
<el-button type="primary" @click="handleSendCode" :disabled="sendState.isCounting">{{
sendState.isCounting ? `${sendState.countdown}s后重新获取` : '发送验证码'
}}</el-button>
</el-form-item>
</el-form>
</div>
<el-button type="primary" @click="handleSubmit" v-auth="'user_transfer'">提交转账</el-button>
</div>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// //
@ -87,7 +135,6 @@ import { getTransferWalletOptionsApi, transferApi } from '@/api/modules/funding/
// //
import { getStorage } from '@/utils/storage' import { getStorage } from '@/utils/storage'
import { useUserStore } from '@/stores/modules/user' import { useUserStore } from '@/stores/modules/user'
const userStore = useUserStore() const userStore = useUserStore()
import { sendCaptcha, initCaptchaState, clearCaptchaTimer } from '@/utils/sendCaptcha' import { sendCaptcha, initCaptchaState, clearCaptchaTimer } from '@/utils/sendCaptcha'
@ -101,14 +148,14 @@ const transferType = ref('internal')
* @description 创建默认转账表单数据 * @description 创建默认转账表单数据
*/ */
const createDefaultTransferForm = () => ({ const createDefaultTransferForm = () => ({
transferOutAccount: -1, transferOutAccount: -1,
transferInAccount: '', transferInAccount: '',
transferAmount: '', transferAmount: '',
remark: '', remark: '',
// //
MaxOutAmount: '', MaxOutAmount: '',
type: '', type: '',
code: '' code: '',
}) })
/** /**
@ -120,11 +167,29 @@ const transferForm = ref(createDefaultTransferForm())
*/ */
const mainTransferFormRef = ref() const mainTransferFormRef = ref()
const verifyFormRef = ref() const verifyFormRef = ref()
const transferFormItemOptions = computed(() => transferType.value === 'internal' ? internalFormItem : externalFormItem) const transferFormItemOptions = computed(() =>
transferType.value === 'internal' ? internalFormItem : externalFormItem,
)
watch(
() => transferForm.value.transferOutAccount,
(val) => {
console.log(
'transferForm.value.transferOutAccount',
val,
transferFormOptionsMap.value.transferOutAccount,
)
transferForm.value.transferInAccount = null
transferFormOptionsMap.value.transferInAccount =
transferFormOptionsMap.value.transferOutAccount.filter((item: any) => item.value !== val)
},
{ inmediate: true, deep: true },
)
const transferFormRules = ref(rules(transferForm.value)) const transferFormRules = ref(rules(transferForm.value))
const transferFormOptionsMap = ref({ const transferFormOptionsMap = ref({
transferOutAccount: [], transferOutAccount: [],
transferInAccount: [], transferInAccount: [],
}) as any }) as any
/** /**
@ -136,13 +201,13 @@ const verifyType = ref('email')
// //
const sendState = ref<any>({ const sendState = ref<any>({
isCounting: false, isCounting: false,
countdown: 0, countdown: 0,
}) })
// //
const emailCaptchaState = initCaptchaState('email'); // const emailCaptchaState = initCaptchaState('email') //
const phoneCaptchaState = initCaptchaState('phone'); // const phoneCaptchaState = initCaptchaState('phone') //
// //
const { isCounting: emailIsCounting, countdown: emailCountdown } = emailCaptchaState const { isCounting: emailIsCounting, countdown: emailCountdown } = emailCaptchaState
@ -150,154 +215,145 @@ const { isCounting: emailIsCounting, countdown: emailCountdown } = emailCaptchaS
// //
const { isCounting: phoneIsCounting, countdown: phoneCountdown } = phoneCaptchaState const { isCounting: phoneIsCounting, countdown: phoneCountdown } = phoneCaptchaState
// sendState // sendState
const updateSendState = (type: string) => { const updateSendState = (type: string) => {
if (type === 'email') { if (type === 'email') {
sendState.value = { sendState.value = {
isCounting: emailIsCounting, isCounting: emailIsCounting,
countdown: emailCountdown countdown: emailCountdown,
}
} else {
sendState.value = {
isCounting: phoneIsCounting,
countdown: phoneCountdown
}
} }
} else {
sendState.value = {
isCounting: phoneIsCounting,
countdown: phoneCountdown,
}
}
} }
// activeTabsendState // activeTabsendState
const initSendState = () => { const initSendState = () => {
const currentType = verifyType.value === 'email' ? 'email' : 'phone' const currentType = verifyType.value === 'email' ? 'email' : 'phone'
updateSendState(currentType) updateSendState(currentType)
} }
onMounted(() => { onMounted(() => {
// //
initSendState() initSendState()
}) })
// verifyTypesendState // verifyTypesendState
watch( watch(
verifyType, verifyType,
(newTab, oldTab) => { (newTab, oldTab) => {
verifyType.value = newTab; verifyType.value = newTab
console.log('verifyType.value', verifyType.value); console.log('verifyType.value', verifyType.value)
// taboldTaboldTabundefined // taboldTaboldTabundefined
if (oldTab === undefined) return if (oldTab === undefined) return
if (newTab === 'email' || newTab === 'phone') { if (newTab === 'email' || newTab === 'phone') {
// / // /
updateSendState(newTab) updateSendState(newTab)
} else if (newTab === 'code') { } else if (newTab === 'code') {
// //
const currentType: string = getStorage('forgotType', 'session') || 'email' const currentType: string = getStorage('forgotType', 'session') || 'email'
updateSendState(currentType) updateSendState(currentType)
} }
}, },
{ immediate: false } // { immediate: false }, //
) )
// //
const sendCodeApi = async () => { const sendCodeApi = async () => {
// //
const sendParams = { const sendParams = {
email: userInfo.email, email: userInfo.email,
mobile: userInfo.mobile, mobile: userInfo.mobile,
reg_type: verifyType.value === 'email' ? 1 : 2, reg_type: verifyType.value === 'email' ? 1 : 2,
type: 'resetPass_code' type: 'resetPass_code',
} }
return userStore.sendCode(sendParams) return userStore.sendCode(sendParams)
} }
// //
const handleSendCode = async () => { const handleSendCode = async () => {
const currentType = verifyType.value; const currentType = verifyType.value
// / // /
const type: string = currentType || 'email'; const type: string = currentType || 'email'
if (type === 'phone') { if (type === 'phone') {
// 1 // 1
if (!userInfo.phone) { if (!userInfo.phone) {
ElMessage.error('请先绑定手机号'); ElMessage.error('请先绑定手机号')
return; return
}
//
await sendCaptcha(
type,
userInfo.phone,
phoneCaptchaState,
() => sendCodeApi()
);
updateSendState(type);
} else {
// 2
if (!userInfo.email) {
ElMessage.error('请先绑定邮箱');
return;
}
//
await sendCaptcha(
type,
userInfo.email,
emailCaptchaState,
() => sendCodeApi()
);
updateSendState(type);
} }
};
//
await sendCaptcha(type, userInfo.phone, phoneCaptchaState, () => sendCodeApi())
updateSendState(type)
} else {
// 2
if (!userInfo.email) {
ElMessage.error('请先绑定邮箱')
return
}
//
await sendCaptcha(type, userInfo.email, emailCaptchaState, () => sendCodeApi())
updateSendState(type)
}
}
// //
onUnmounted(() => { onUnmounted(() => {
clearCaptchaTimer(emailCaptchaState); clearCaptchaTimer(emailCaptchaState)
clearCaptchaTimer(phoneCaptchaState); clearCaptchaTimer(phoneCaptchaState)
}); })
/** /**
* @description 监听transferOutAccount变化,获取对应的MaxAmount * @description 监听transferOutAccount变化,获取对应的MaxAmount
*/ */
watch( watch(
[ [
() => transferForm.value.transferOutAccount, () => transferForm.value.transferOutAccount,
() => transferFormOptionsMap.value.transferOutAccount () => transferFormOptionsMap.value.transferOutAccount,
], ],
([newVal]) => { ([newVal]) => {
if (!newVal || !transferFormOptionsMap.value.transferOutAccount.length) return if (!newVal || !transferFormOptionsMap.value.transferOutAccount.length) return
const item = transferFormOptionsMap.value.transferOutAccount.find( const item = transferFormOptionsMap.value.transferOutAccount.find(
(item: any) => item.value === newVal (item: any) => item.value === newVal,
) )
transferForm.value.MaxOutAmount = item?.amount || 0 transferForm.value.MaxOutAmount = item?.amount || 0
}, },
{ immediate: true } { immediate: true },
) )
/** /**
* @description 统一获取数据 * @description 统一获取数据
*/ */
const getTransferWalletOptions = async () => { const getTransferWalletOptions = async () => {
const data: any = await getTransferWalletOptionsApi() const data: any = await getTransferWalletOptionsApi()
transferFormOptionsMap.value.transferOutAccount = data transferFormOptionsMap.value.transferOutAccount = data
transferFormOptionsMap.value.transferInAccount = data transferFormOptionsMap.value.transferInAccount = data.filter((item: any) => item.value !== -1)
} }
/** /**
* @description 页面加载完成需要渲染的数据 * @description 页面加载完成需要渲染的数据
*/ */
onMounted(() => { onMounted(() => {
getTransferWalletOptions() getTransferWalletOptions()
}) })
// //
const getDefaultMaxOutAmount = () => { const getDefaultMaxOutAmount = () => {
return transferFormOptionsMap.value.transferOutAccount.find((item: any) => item.value === -1)?.amount || '' return (
transferFormOptionsMap.value.transferOutAccount.find((item: any) => item.value === -1)
?.amount || ''
)
} }
// / // /
const resetTransferForm = async () => { const resetTransferForm = async () => {
Object.assign(transferForm.value, createDefaultTransferForm(), { Object.assign(transferForm.value, createDefaultTransferForm(), {
MaxOutAmount: getDefaultMaxOutAmount(), MaxOutAmount: getDefaultMaxOutAmount(),
}) })
await nextTick() await nextTick()
verifyFormRef.value?.clearValidate?.() verifyFormRef.value?.clearValidate?.()
} }
/** /**
@ -305,153 +361,155 @@ const resetTransferForm = async () => {
* @param type 转账类型 * @param type 转账类型
*/ */
const handleTransferTabClick = async (type: string) => { const handleTransferTabClick = async (type: string) => {
// / // /
if (transferType.value === type) return if (transferType.value === type) return
transferType.value = type transferType.value = type
await resetTransferForm() await resetTransferForm()
} }
/** /**
* @description 切换验证方式 * @description 切换验证方式
* @param type 验证方式 * @param type 验证方式
*/ */
const handleVerifyTabClick = (type: string) => { const handleVerifyTabClick = (type: string) => {
verifyType.value = type verifyType.value = type
} }
const handleVerifyParams = (type: string) => { const handleVerifyParams = (type: string) => {
if (type === 'external') { if (type === 'external') {
if (verifyType.value === 'email') { if (verifyType.value === 'email') {
return userInfo.email return userInfo.email
} else {
return userInfo.phone
}
} else { } else {
return '1' return userInfo.phone
} }
} else {
return transferForm.value.transferInAccount
}
} }
/** /**
* @description 提交转账表单 * @description 提交转账表单
*/ */
const handleSubmit = async () => { const handleSubmit = async () => {
await mainTransferFormRef.value?.validate?.() await mainTransferFormRef.value?.validate?.()
if (transferType.value === 'external') { if (transferType.value === 'external') {
await verifyFormRef.value?.validate?.() await verifyFormRef.value?.validate?.()
} }
const params = { const params = {
type: transferType.value === 'internal' ? '1' : '2', type: transferType.value === 'internal' ? '1' : '2',
from: String(transferForm.value.transferOutAccount), from: String(transferForm.value.transferOutAccount) + '',
to: handleVerifyParams(transferType.value), to: handleVerifyParams(transferType.value) + '',
amount: transferForm.value.transferAmount, amount: transferForm.value.transferAmount,
verify_type: '', verify_type: '',
verify_code: '', verify_code: '',
description: transferForm.value.remark, description: transferForm.value.remark,
} }
console.log(params) console.log(params)
if (transferType.value === 'internal') { if (transferType.value === 'internal') {
await transferApi(params) await transferApi(params)
} else { } else {
await transferApi({ ...params, verify_type: verifyType.value === 'email' ? '1' : '2', verify_code: transferForm.value.code }) await transferApi({
} ...params,
verify_type: verifyType.value === 'email' ? '1' : '2',
verify_code: transferForm.value.code,
})
}
await resetTransferForm() await resetTransferForm()
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.transfer { .transfer {
.title { .title {
margin-bottom: 10px; margin-bottom: 10px;
}
.transfer-card {
background: rgb(160, 207, 255, 0.5);
margin-bottom: 10px;
:deep(.el-card__body) {
padding: 10px 20px;
} }
.transfer-card { .transfer-item {
background: rgb(160, 207, 255, 0.5); line-height: 35px;
}
}
.transfer-type {
margin-bottom: 10px;
.type-title {
margin-bottom: 10px;
}
}
:deep(.el-form) {
.el-form-item {
width: 400px;
}
}
.transfer-form {
//
:deep(.transfer-main-form .el-form-item) {
margin-bottom: 24px;
}
:deep(.transfer-main-form .el-form-item__error) {
position: static;
padding-top: 4px;
line-height: 18px;
}
.external-item {
margin-bottom: 10px;
.externa-title {
margin-bottom: 10px; margin-bottom: 10px;
}
.external-card {
width: fit-content;
:deep(.el-card__body) { :deep(.el-card__body) {
padding: 10px 20px; display: flex;
align-items: center;
padding: 10px 20px;
} }
.transfer-item { .el-icon {
line-height: 35px; color: #409eff;
font-size: 34px;
margin-right: 10px;
} }
.money {
display: flex;
flex-direction: column;
}
}
} }
}
.transfer-type { .verify {
margin-bottom: 10px; .tab,
.username {
.type-title { margin-bottom: 10px;
margin-bottom: 10px;
}
} }
}
:deep(.el-form) { .verify-form {
.el-form-item { .el-form-item {
width: 400px; :deep(.el-form-item__content) {
} flex-wrap: nowrap;
} }
.transfer-form {
//
:deep(.transfer-main-form .el-form-item) {
margin-bottom: 24px;
}
:deep(.transfer-main-form .el-form-item__error) {
position: static;
padding-top: 4px;
line-height: 18px;
}
.external-item {
margin-bottom: 10px;
.externa-title {
margin-bottom: 10px;
}
.external-card {
width: fit-content;
:deep(.el-card__body) {
display: flex;
align-items: center;
padding: 10px 20px;
}
.el-icon {
color: #409eff;
font-size: 34px;
margin-right: 10px;
}
.money {
display: flex;
flex-direction: column;
}
}
}
}
.verify {
.tab,
.username {
margin-bottom: 10px;
}
}
.verify-form {
.el-form-item {
:deep(.el-form-item__content) {
flex-wrap: nowrap;
}
}
} }
}
} }
</style> </style>

View File

@ -97,7 +97,13 @@
<el-card class="account"> <el-card class="account">
<h3 class="account-title"> <h3 class="account-title">
个人账户 个人账户
<el-button type="primary" size="small" @click="handleAddSubAccount">添加子账户</el-button> <el-button
type="primary"
size="small"
@click="handleAddSubAccount"
v-auth="'user_createAccount'"
>添加子账户</el-button
>
</h3> </h3>
<div class="account-list"> <div class="account-list">
<div class="list" v-for="item in accountList" :key="item.id"> <div class="list" v-for="item in accountList" :key="item.id">

View File

@ -2,11 +2,21 @@
<div class="mould-fee"> <div class="mould-fee">
<header> <header>
<div class="header-actions"> <div class="header-actions">
<el-button type="primary" @click="addFee">添加模板</el-button> <el-button type="primary" @click="addFee" v-auth="'feeHandl_createFeeSetting'"
<el-button type="primary" @click="editFee">编辑模板</el-button> >添加模板</el-button
<el-button type="primary" @click="changeStatus(1)">启用模板</el-button> >
<el-button type="danger" @click="changeStatus(2)">禁用模板</el-button> <el-button type="primary" @click="editFee" v-auth="'feeHandl_editFeeSetting'"
<el-button type="primary" @click="editFeeDetail">编辑模板详情</el-button> >编辑模板</el-button
>
<el-button type="primary" @click="changeStatus(1)" v-auth="'feeHandl_editFeeSetting'"
>启用模板</el-button
>
<el-button type="danger" @click="changeStatus(2)" v-auth="'feeHandl_editFeeSetting'"
>禁用模板</el-button
>
<el-button type="primary" @click="editFeeDetail" v-auth="'feeHandl_setFeeSettingDetail'"
>编辑模板详情</el-button
>
</div> </div>
</header> </header>
<div class="table-container"> <div class="table-container">
@ -90,7 +100,6 @@ import {
import Form from '@/components/common/Form.vue' import Form from '@/components/common/Form.vue'
import { formDataList, formRulesList } from './option.ts' import { formDataList, formRulesList } from './option.ts'
const dialogVisible = ref(false) const dialogVisible = ref(false)
const dialogTitleType = ref(1) const dialogTitleType = ref(1)
const formData = ref({ const formData = ref({

View File

@ -2,9 +2,27 @@
<div class="risk"> <div class="risk">
<div class="bar"> <div class="bar">
<span>风控管理</span> <span>风控管理</span>
<el-button type="primary" @click="handleAddRisk">添加</el-button> <el-button type="primary" @click="handleAddRisk" v-auth="'riskControl_createRiskControl'"
<el-button type="primary" @click="handleModifyRisk">修改</el-button> >添加</el-button
<el-button type="danger" @click="handleRiskStatus">停用</el-button> >
<el-button
type="primary"
@click="handleModifyRisk"
v-auth="'riskControl_editRiskControlSetting'"
>修改</el-button
>
<el-button
type="danger"
@click="handleChangeStatus(2)"
v-auth="'riskControl_editRiskControlSetting'"
>停用</el-button
>
<el-button
type="primary"
@click="handleChangeStatus(1)"
v-auth="'riskControl_editRiskControlSetting'"
>启用</el-button
>
</div> </div>
<!-- 表格 --> <!-- 表格 -->
<div class="risk-table-content"> <div class="risk-table-content">
@ -243,10 +261,10 @@ const handleModifyRisk = () => {
} as any } as any
} }
const handleRiskStatus = async () => { const handleChangeStatus = async (status) => {
dialogTitle.value = '停用' dialogTitle.value = '停用'
dialogType.value = 'status' dialogType.value = 'status'
await editRiskApi({ ...selectedRow.value, status: 2 }) await editRiskApi({ ...selectedRow.value, status: status })
getRiskList() getRiskList()
} }

View File

@ -42,13 +42,18 @@ import { getTransferDetailListApi } from '@/api/modules/funding/transferDetail'
// //
import { exportToExcel } from '@/utils/exportToExcel' import { exportToExcel } from '@/utils/exportToExcel'
import { getRecentSevenDays } from '@/utils/handleTime.ts'
const initTime = getRecentSevenDays()
/** /**
* @description: 定义搜索数据 * @description: 定义搜索数据
*/ */
const searchForm = ref({ const searchForm = ref({
transferAccount: '', transferAccount: '',
type: '', type: '',
transferTime: '', transferTime: [initTime.start, initTime.end],
}) })
const searchOptions = ref(search) const searchOptions = ref(search)
/** /**
@ -66,6 +71,7 @@ const total = ref(0)
/** /**
* @description: 定义公共请求数据方法 * @description: 定义公共请求数据方法
*/ */
const getTransferDetailList = async () => { const getTransferDetailList = async () => {
const params = { const params = {
page: currentPage.value, page: currentPage.value,

View File

@ -20,6 +20,7 @@
v-if="row.role_id === 5" v-if="row.role_id === 5"
type="primary" type="primary"
size="small" size="small"
v-auth="'agent_getSelfVarietyPointDiffConfig'"
@click="handleMarginRatio(row)" @click="handleMarginRatio(row)"
> >
修改 修改
@ -32,6 +33,7 @@
type="primary" type="primary"
size="small" size="small"
@click="handleCommissionRatio(row)" @click="handleCommissionRatio(row)"
v-auth="'agent_editAgent'"
> >
修改 修改
</el-button> </el-button>

View File

@ -8,8 +8,15 @@
@reset="handleReset" @reset="handleReset"
> >
<template #button="{ form }"> <template #button="{ form }">
<el-button type="primary" @click="handleAddMarket()"> 添加 </el-button> <el-button type="primary" @click="handleAddMarket()" v-auth="'agent_createAgen'">
<el-button type="primary" :disabled="!selectedRow" @click="handleEditMarket()"> 添加
</el-button>
<el-button
type="primary"
:disabled="!selectedRow"
v-auth="'agent_editAgent'"
@click="handleEditMarket()"
>
编辑 编辑
</el-button> </el-button>
</template> </template>

View File

@ -1,68 +1,97 @@
<template> <template>
<div class="client"> <div class="client">
<!-- 搜索组件 --> <!-- 搜索组件 -->
<ClientSearch :search-form="clientSearchForm" :search-options="clientSearchOptions" @search="handleSearch" <ClientSearch
@reset="handleReset"> :search-form="clientSearchForm"
<template #button="{ form }"> :search-options="clientSearchOptions"
<el-button type="primary" @click="handleAdd()"> @search="handleSearch"
添加客户 @reset="handleReset"
</el-button> >
</template> <template #button="{ form }">
</ClientSearch> <el-button type="primary" @click="handleAdd()" v-auth="'user_createUser'">
添加客户
</el-button>
</template>
</ClientSearch>
<!-- 表格组件 --> <!-- 表格组件 -->
<ClientTable :table-data="clientTree" :columns="clientTableColumns"> <ClientTable :table-data="clientTree" :columns="clientTableColumns">
<template #status="{ row }"> <template #status="{ row }">
{{row.status === 1 ? '正常' : '禁用'}} {{ row.status === 1 ? '正常' : '禁用' }}
</template> </template>
<template #action="{ row }"> <template #action="{ row }">
<el-button type="primary" size="small" @click="handleChange(row)"> <el-button type="primary" size="small" @click="handleChange(row)" v-anth="'user_editUser'">
修改 修改
</el-button> </el-button>
</template> </template>
</ClientTable> </ClientTable>
<!-- 分页组件 --> <!-- 分页组件 -->
<ClientPagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total" <ClientPagination
@pagination-change="handlePaginationChange" /> v-model="currentPage"
</div> v-model:pageSizeValue="pageSize"
<!-- 客户弹窗 --> :total="total"
<ClientDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" @confirm="handleSubmit" @pagination-change="handlePaginationChange"
@cancel="handleCancel" @close="handleClose"> />
<ClientForm :form-data="clientForm" :form-rules="clientFormRules" :form-items="clientFormOptions" </div>
:options-map="clientFormOptionsMap" ref="clientFormRef"> <!-- 客户弹窗 -->
<template #phoneOrEmail="{ formData }" style="margin-left: 0;"> <ClientDialog
<div class="phone-email"> :visible="dialogVisible"
<div class="tab-btn"> :title="dialogTitle"
<el-button :type="activeTab === 'email' ? 'primary' : 'default'" size="small" :type="dialogType"
@click="activeTab = 'email'"> @confirm="handleSubmit"
邮箱 @cancel="handleCancel"
</el-button> @close="handleClose"
<el-button :type="activeTab === 'phone' ? 'primary' : 'default'" size="small" >
@click="activeTab = 'phone'"> <ClientForm
手机号 :form-data="clientForm"
</el-button> :form-rules="clientFormRules"
</div> :form-items="clientFormOptions"
<div class="group"> :options-map="clientFormOptionsMap"
<el-form-item prop="email" v-if="activeTab === 'email'"> ref="clientFormRef"
<el-input v-model="clientForm.email" placeholder="请输入邮箱" /> >
</el-form-item> <template #phoneOrEmail="{ formData }" style="margin-left: 0">
<el-form-item prop="phone" class="phone-item" v-if="activeTab === 'phone'"> <div class="phone-email">
<el-select v-model="clientForm.countryCode" placeholder="请选择国家区号"> <div class="tab-btn">
<el-option v-for="item in countryOptions" :key="item.code" :label="item.code" <el-button
:value="item.code"> :type="activeTab === 'email' ? 'primary' : 'default'"
<div class="country-option"> size="small"
<span class="country-name">{{ item.name }}</span> @click="activeTab = 'email'"
<span class="country-code">{{ item.code }}</span> >
</div> 邮箱
</el-option> </el-button>
</el-select> <el-button
<el-input v-model="clientForm.phone" placeholder="请输入手机号" /> :type="activeTab === 'phone' ? 'primary' : 'default'"
</el-form-item> size="small"
</div> @click="activeTab = 'phone'"
</div> >
</template> 手机号
</ClientForm> </el-button>
</ClientDialog> </div>
<div class="group">
<el-form-item prop="email" v-if="activeTab === 'email'">
<el-input v-model="clientForm.email" placeholder="请输入邮箱" />
</el-form-item>
<el-form-item prop="phone" class="phone-item" v-if="activeTab === 'phone'">
<el-select v-model="clientForm.countryCode" placeholder="请选择国家区号">
<el-option
v-for="item in countryOptions"
:key="item.code"
:label="item.code"
:value="item.code"
>
<div class="country-option">
<span class="country-name">{{ item.name }}</span>
<span class="country-code">{{ item.code }}</span>
</div>
</el-option>
</el-select>
<el-input v-model="clientForm.phone" placeholder="请输入手机号" />
</el-form-item>
</div>
</div>
</template>
</ClientForm>
</ClientDialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -73,10 +102,23 @@ import ClientPagination from '@/components/common/Pagination.vue'
import ClientDialog from '@/components/common/Dialog.vue' import ClientDialog from '@/components/common/Dialog.vue'
import ClientForm from '@/components/common/Form.vue' import ClientForm from '@/components/common/Form.vue'
// //
import { getClientListApi, getFeeTemplateOptionsApi, getRiskTemplateOptionsApi, getAgentOptionsApi, createClientApi, editClientApi } from '@/api/modules/account/client' import {
getClientListApi,
getFeeTemplateOptionsApi,
getRiskTemplateOptionsApi,
getAgentOptionsApi,
createClientApi,
editClientApi,
} from '@/api/modules/account/client'
// //
import { clientSearch, clientAddFormItem, clientChangeFormItem, countryOptions, clientTableColumns } from './options' import {
clientSearch,
clientAddFormItem,
clientChangeFormItem,
countryOptions,
clientTableColumns,
} from './options'
import { rules } from './rules' import { rules } from './rules'
/** /**
@ -84,10 +126,10 @@ import { rules } from './rules'
*/ */
const clientSearchOptions = ref(clientSearch) const clientSearchOptions = ref(clientSearch)
const clientSearchForm = ref({ const clientSearchForm = ref({
username: '', username: '',
status: 1, status: 1,
startDateTime: '', startDateTime: '',
endDateTime: '', endDateTime: '',
}) })
/** /**
@ -112,6 +154,160 @@ const dialogType = ref('add')
* @description 定义dialog中form的数据 * @description 定义dialog中form的数据
*/ */
const clientForm = ref({ const clientForm = ref({
//
feeTemplate: '',
//
riskTemplate: '',
//
agentUsername: '',
email: '',
phone: '',
countryCode: '+86',
//
isUpgrade: 1,
//
userName: '',
status: 1,
})
//
const clientFormRef = ref()
const clientFormRules = ref(rules())
//
const optionsFetched = ref(false)
const useId = ref('')
const clientFormOptions = computed(() => {
return dialogType.value === 'add' ? clientAddFormItem : clientChangeFormItem
})
const clientFormOptionsMap = ref<any>({
feeTemplate: [],
riskTemplate: [],
agentUsername: [],
isUpgrade: [
{ label: '是', value: 1 },
{ label: '否', value: 2 },
],
status: [
{ label: '正常', value: 1 },
{ label: '禁用', value: 2 },
],
})
//
const activeTab = ref('email')
/**
* @description 统一数据处理方法
*/
//
const mapObjectToOptions = (obj: Record<string, string>) => {
return Object.entries(obj).map(([label, value]) => ({
label, // /
value, // /ID
}))
}
/**
* @description 统一获取数据方法
*/
const getClientList = async () => {
const params = {
page: currentPage.value,
page_size: pageSize.value,
username: clientSearchForm.value.username,
status: clientSearchForm.value.status || '',
reg_start_time:
clientSearchForm.value.startDateTime.length > 0
? `${clientSearchForm.value.startDateTime} 0:00:00`
: '',
reg_end_time:
clientSearchForm.value.endDateTime.length > 0
? `${clientSearchForm.value.endDateTime} 23:59:59`
: '',
}
const data = await getClientListApi(params)
//
clientTree.value = data.data.map((item) => ({
...item,
//
createdTime: item.created_at,
//
agentUsername: item.par_uid === item.parent?.id ? item.parent?.username : '',
}))
//
total.value = Number(data.total)
currentPage.value = Number(data.current_page)
pageSize.value = Number(data.per_page)
}
//
const getOptions = async () => {
//
if (optionsFetched.value) return
try {
const [agentRes, feeTemplateRes, riskTemplateRes] = await Promise.all([
getAgentOptionsApi(),
getFeeTemplateOptionsApi(),
getRiskTemplateOptionsApi(),
])
// label=value=ID
clientFormOptionsMap.value.agentUsername = mapObjectToOptions(
agentRes as Record<string, string>,
)
clientFormOptionsMap.value.feeTemplate = mapObjectToOptions(
feeTemplateRes as Record<string, string>,
)
clientFormOptionsMap.value.riskTemplate = mapObjectToOptions(
riskTemplateRes as Record<string, string>,
)
optionsFetched.value = true //
} catch (error) {
console.error('获取表单选项失败:', error)
}
}
//
/**
* @description 页面加载后获取数据
*/
onMounted(async () => {
await getClientList()
})
/**
* @description 搜索方法
*/
const handleSearch = async () => {
try {
await getClientList()
} catch (err) {
console.error('获取客户列表失败', err)
}
}
const handleReset = async () => {
//
clientSearchForm.value = {
username: '',
status: '正常',
startDateTime: '',
endDateTime: '',
}
try {
await getClientList()
} catch (err) {
console.error('获取客户列表失败', err)
}
}
//
const handleAdd = () => {
//
dialogVisible.value = true
dialogTitle.value = '添加客户'
dialogType.value = 'add'
clientForm.value = {
// //
feeTemplate: '', feeTemplate: '',
// //
@ -126,263 +322,121 @@ const clientForm = ref({
// //
userName: '', userName: '',
status: 1, status: 1,
}) }
getOptions()
//
const clientFormRef = ref()
const clientFormRules = ref(rules())
//
const optionsFetched = ref(false)
const useId = ref('')
const clientFormOptions = computed(() => {
return dialogType.value === 'add' ? clientAddFormItem : clientChangeFormItem;
})
const clientFormOptionsMap = ref<any>({
feeTemplate: [],
riskTemplate: [],
agentUsername: [],
isUpgrade: [
{ label: '是', value: 1 },
{ label: '否', value: 2 }
],
status: [
{ label: '正常', value: 1 },
{ label: '禁用', value: 2 }
],
})
//
const activeTab = ref('email')
/**
* @description 统一数据处理方法
*/
//
const mapObjectToOptions = (obj: Record<string, string>) => {
return Object.entries(obj).map(([label, value]) => ({
label, // /
value // /ID
}))
}
/**
* @description 统一获取数据方法
*/
const getClientList = async () => {
const params = {
page: currentPage.value,
page_size: pageSize.value,
username: clientSearchForm.value.username,
status: clientSearchForm.value.status || '',
reg_start_time: clientSearchForm.value.startDateTime.length > 0 ? `${clientSearchForm.value.startDateTime} 0:00:00` : '',
reg_end_time: clientSearchForm.value.endDateTime.length > 0 ? `${clientSearchForm.value.endDateTime} 23:59:59` : '',
}
const data = await getClientListApi(params)
//
clientTree.value = data.data.map(item => ({
...item,
//
createdTime: item.created_at,
//
agentUsername: item.par_uid === item.parent?.id ? item.parent?.username : '',
}));
//
total.value = Number(data.total)
currentPage.value = Number(data.current_page)
pageSize.value = Number(data.per_page)
}
//
const getOptions = async () => {
//
if (optionsFetched.value) return
try {
const [agentRes, feeTemplateRes, riskTemplateRes] = await Promise.all([
getAgentOptionsApi(),
getFeeTemplateOptionsApi(),
getRiskTemplateOptionsApi()
])
// label=value=ID
clientFormOptionsMap.value.agentUsername = mapObjectToOptions(agentRes as Record<string, string>)
clientFormOptionsMap.value.feeTemplate = mapObjectToOptions(feeTemplateRes as Record<string, string>)
clientFormOptionsMap.value.riskTemplate = mapObjectToOptions(riskTemplateRes as Record<string, string>)
optionsFetched.value = true //
} catch (error) {
console.error('获取表单选项失败:', error)
}
}
//
/**
* @description 页面加载后获取数据
*/
onMounted(async () => {
await getClientList()
})
/**
* @description 搜索方法
*/
const handleSearch = async () => {
try {
await getClientList()
} catch (err) {
console.error('获取客户列表失败', err)
}
}
const handleReset = async () => {
//
clientSearchForm.value = {
username: '',
status: '正常',
startDateTime: '',
endDateTime: '',
}
try {
await getClientList()
} catch (err) {
console.error('获取客户列表失败', err)
}
}
//
const handleAdd = () => {
//
dialogVisible.value = true
dialogTitle.value = '添加客户'
dialogType.value = 'add'
clientForm.value = {
//
feeTemplate: '',
//
riskTemplate: '',
//
agentUsername: '',
email: '',
phone: '',
countryCode: '+86',
//
isUpgrade: 1,
//
userName: '',
status: 1,
}
getOptions()
} }
/** /**
* @description 表格方法 * @description 表格方法
*/ */
const handleChange = (row: any) => { const handleChange = (row: any) => {
// //
dialogVisible.value = true dialogVisible.value = true
dialogTitle.value = '修改客户' dialogTitle.value = '修改客户'
dialogType.value = 'change' dialogType.value = 'change'
useId.value = row.id useId.value = row.id
clientForm.value = { clientForm.value = {
// //
feeTemplate: row.wallet_capital.fee_setting_id, feeTemplate: row.wallet_capital.fee_setting_id,
// //
riskTemplate: row.wallet_capital.risk_control_id, riskTemplate: row.wallet_capital.risk_control_id,
userName: row.username, userName: row.username,
status: row.status, status: row.status,
isUpgrade: 2, isUpgrade: 2,
} as any } as any
console.log('rowrowrowrowrowrowrowrowrow', clientForm.value) console.log('rowrowrowrowrowrowrowrowrow', clientForm.value)
getOptions() getOptions()
} }
/** /**
* @description 分页方法 * @description 分页方法
*/ */
const handlePaginationChange = (page: { currentPage: number, pageSize: number }) => { const handlePaginationChange = (page: { currentPage: number; pageSize: number }) => {
currentPage.value = page.currentPage currentPage.value = page.currentPage
pageSize.value = page.pageSize pageSize.value = page.pageSize
handleSearch() handleSearch()
} }
/** /**
* @description 客户弹窗方法 * @description 客户弹窗方法
*/ */
const handleSubmit = async () => { const handleSubmit = async () => {
try { try {
await clientFormRef.value?.validate() await clientFormRef.value?.validate()
if (dialogType.value === 'add') { if (dialogType.value === 'add') {
const params = { const params = {
reg_type: activeTab.value === 'email' ? 1 : 2, reg_type: activeTab.value === 'email' ? 1 : 2,
email: clientForm.value.email, email: clientForm.value.email,
mobile: clientForm.value.phone, mobile: clientForm.value.phone,
parent_id: clientForm.value.agentUsername, parent_id: clientForm.value.agentUsername,
risk_control_id: clientForm.value.riskTemplate, risk_control_id: clientForm.value.riskTemplate,
fee_setting_id: clientForm.value.feeTemplate, fee_setting_id: clientForm.value.feeTemplate,
} }
await createClientApi(params) await createClientApi(params)
await getClientList() await getClientList()
dialogVisible.value = false dialogVisible.value = false
} else { } else {
const params = { const params = {
user_id: useId.value, user_id: useId.value,
risk_control_id: clientForm.value.riskTemplate, risk_control_id: clientForm.value.riskTemplate,
fee_setting_id: clientForm.value.feeTemplate, fee_setting_id: clientForm.value.feeTemplate,
username: clientForm.value.userName, username: clientForm.value.userName,
is_levelup: clientForm.value.isUpgrade, is_levelup: clientForm.value.isUpgrade,
status: clientForm.value.status, status: clientForm.value.status,
} }
await editClientApi(params) await editClientApi(params)
await getClientList() await getClientList()
dialogVisible.value = false dialogVisible.value = false
}
} catch (err) {
console.error('客户表单验证失败', err)
} }
} catch (err) {
console.error('客户表单验证失败', err)
}
} }
const handleCancel = () => { const handleCancel = () => {
// //
dialogVisible.value = false dialogVisible.value = false
} }
const handleClose = () => { const handleClose = () => {
// //
dialogVisible.value = false dialogVisible.value = false
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
:deep(.el-form-item__content) { :deep(.el-form-item__content) {
margin-left: 0 !important; margin-left: 0 !important;
} }
.phone-email { .phone-email {
display: flex;
flex: 1;
.tab-btn {
display: flex;
margin-right: 10px;
.el-button {
height: 32px;
margin-left: 0;
}
}
.group {
display: flex; display: flex;
flex: 1; flex: 1;
.tab-btn { .el-form-item {
display: flex; flex: 1;
margin-right: 10px;
.el-button {
height: 32px;
margin-left: 0;
}
} }
.group { .phone-item {
display: flex; :deep(.el-form-item__content) {
flex: 1; flex-wrap: nowrap;
}
.el-form-item { .el-select {
flex: 1; width: 120px;
} }
.phone-item {
:deep(.el-form-item__content) {
flex-wrap: nowrap;
}
.el-select {
width: 120px;
}
}
} }
}
} }
</style> </style>

View File

@ -1,41 +1,68 @@
<template> <template>
<div class="system"> <div class="system">
<!-- 菜单列表头 --> <!-- 菜单列表头 -->
<div class="header"> <div class="header">
<h4>菜单列表</h4> <h4>菜单列表</h4>
<el-button type="primary" @click="handleAddMenu">新增菜单</el-button> <el-button type="primary" @click="handleAddMenu" v-auth="'menus_create'">新增菜单</el-button>
</div>
<!-- 菜单列表表格 -->
<MenuTable :table-data="menuTree" :columns="menuTableColumns" row-key="id" :tree-props="treeProps">
<template #icon="{ row }">
<el-icon v-if="row.icon && menuiconMap[row.icon]">
<component :is="menuiconMap[row.icon]" />
</el-icon>
<span v-else>-</span>
</template>
<template #type="{ row }">
{{ row.type === 1 ? '目录' : '按钮' }}
</template>
<template #status="{ row }">
<el-switch :model-value="row.status === 1" :active-value="true" :inactive-value="false" active-text="启用"
inactive-text="禁用"/>
</template>
<template #action="{ row }">
<el-button link type="primary" size="small" @click="handleEditMenu(row)">
编辑
</el-button>
<el-button link type="danger" size="small" @click="handleDeleteMenu(row)">
删除
</el-button>
</template>
</MenuTable>
</div> </div>
<!-- 菜单新增/编辑弹窗 --> <!-- 菜单列表表格 -->
<MenuDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" @confirm="handleSubmit" <MenuTable
@cancel="handleCancel" @close="handleClose"> :table-data="menuTree"
<MenuForm :form-data="menuForm" :form-rules="menuFormRules" :form-items="menuFormItemOptions" :columns="menuTableColumns"
:options-map="menuFormOptionsMap" ref="menuFormRef" /> row-key="id"
</MenuDialog> :tree-props="treeProps"
>
<template #icon="{ row }">
<el-icon v-if="row.icon && menuiconMap[row.icon]">
<component :is="menuiconMap[row.icon]" />
</el-icon>
<span v-else>-</span>
</template>
<template #type="{ row }">
{{ row.type === 1 ? '目录' : '按钮' }}
</template>
<template #status="{ row }">
{{ row.status === 1 ? '启用' : '禁用' }}
</template>
<template #action="{ row }">
<el-button
link
type="primary"
size="small"
@click="handleEditMenu(row)"
v-auth="'menus_edit'"
>
编辑
</el-button>
<el-button
link
type="danger"
size="small"
@click="handleDeleteMenu(row)"
v-auth="'menus_del'"
>
删除
</el-button>
</template>
</MenuTable>
</div>
<!-- 菜单新增/编辑弹窗 -->
<MenuDialog
:visible="dialogVisible"
:title="dialogTitle"
:type="dialogType"
@confirm="handleSubmit"
@cancel="handleCancel"
@close="handleClose"
>
<MenuForm
:form-data="menuForm"
:form-rules="menuFormRules"
:form-items="menuFormItemOptions"
:options-map="menuFormOptionsMap"
ref="menuFormRef"
/>
</MenuDialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
@ -52,7 +79,7 @@ import { getMenuApi, createMenuApi, editMenuApi, deleteMenuApi } from '@/api/mod
// header // header
const menuTitle = ref('菜单管理') const menuTitle = ref('菜单管理')
const menuHeaderButtons = [ const menuHeaderButtons = [
{ label: '新增菜单', type: 'primary' as const, icon: Plus, key: 'addMenu' }, { label: '新增菜单', type: 'primary' as const, icon: Plus, key: 'addMenu' },
] ]
// //
@ -66,39 +93,39 @@ const dialogType = ref('add')
// //
const menuForm = ref({ const menuForm = ref({
name: '', // name: '', //
parentName: '', // parentName: '', //
path: '', // path: '', //
api: '', // api: '', //
icon: '', // icon: '', //
sort: null, // 1 sort: null, // 1
type: null, // type: null, //
status: null, // status: null, //
}) })
const menuFormRef = ref<FormInstance>() const menuFormRef = ref<FormInstance>()
// //
const menuFormRules = rules(menuForm.value, menuiconOptions) const menuFormRules = rules(menuForm.value, menuiconOptions)
// icon // icon
const menuFormOptionsMap = ref({ const menuFormOptionsMap = ref({
'parentName': [] as any[], parentName: [] as any[],
'icon': menuiconOptions, icon: menuiconOptions,
'type': [ type: [
{ label: '菜单', value: 1 }, { label: '菜单', value: 1 },
{ label: '按钮', value: 2 } { label: '按钮', value: 2 },
], ],
'status': [ status: [
{ label: '启用', value: 1 }, { label: '启用', value: 1 },
{ label: '禁用', value: 2 } { label: '禁用', value: 2 },
] ],
}) })
/** /**
@description: 新增菜单 @description: 新增菜单
**/ **/
const handleAddMenu = () => { const handleAddMenu = () => {
dialogVisible.value = true dialogVisible.value = true
dialogTitle.value = '新增菜单' dialogTitle.value = '新增菜单'
menuForm.value.status = 1 as any // menuForm.value.status = 1 as any //
} }
/** /**
@ -107,108 +134,105 @@ const handleAddMenu = () => {
// //
const fetchMenuList = async () => { const fetchMenuList = async () => {
try { try {
const data = await getMenuApi() const data = await getMenuApi()
menuTree.value = data menuTree.value = data
// parentName // parentName
const parentNameOptions = data.map(item => ({ const parentNameOptions = data.map((item) => ({
label: item.name, label: item.name,
value: item.id ?? '' value: item.id ?? '',
})) }))
menuFormOptionsMap.value.parentName = [ menuFormOptionsMap.value.parentName = [{ label: '一级菜单', value: '0' }, ...parentNameOptions]
{ label: '一级菜单', value: "0" }, } catch (err) {
...parentNameOptions console.error('获取菜单列表失败', err)
] }
} catch (err) {
console.error('获取菜单列表失败', err)
}
} }
// //
onMounted(async () => { onMounted(async () => {
await fetchMenuList() await fetchMenuList()
}) })
/** /**
@description: 处理菜单编辑/删除/状态切换 @description: 处理菜单编辑/删除/状态切换
**/ **/
const handleEditMenu = (row: any) => { const handleEditMenu = (row: any) => {
console.log('编辑菜单', row) console.log('编辑菜单', row)
dialogVisible.value = true dialogVisible.value = true
dialogTitle.value = '编辑菜单' dialogTitle.value = '编辑菜单'
dialogType.value = 'edit' dialogType.value = 'edit'
menuForm.value = { menuForm.value = {
...row, ...row,
id: row.id, // id id: row.id, // id
parentName: row.name, parentName: row.name,
status: row.status ?? 1, // status: row.status ?? 1, //
api: row.api, // api: row.api, //
} }
} }
const handleDeleteMenu = async (row: any) => { const handleDeleteMenu = async (row: any) => {
try { try {
await deleteMenuApi({ id: row.id }) await deleteMenuApi({ id: row.id })
await fetchMenuList() await fetchMenuList()
} catch (err) { } catch (err) {
console.error('删除菜单失败') console.error('删除菜单失败')
} }
} }
/** /**
@description: 处理弹窗提交/取消/关闭事件 @description: 处理弹窗提交/取消/关闭事件
**/ **/
const handleSubmit = async () => { const handleSubmit = async () => {
console.log('提交菜单表单') console.log('提交菜单表单')
try { try {
await menuFormRef.value?.validate() await menuFormRef.value?.validate()
if (dialogType.value === 'add') { if (dialogType.value === 'add') {
const params = { const params = {
name: menuForm.value.name, // name: menuForm.value.name, //
parent_id: menuForm.value.parentName, // id parent_id: menuForm.value.parentName, // id
path: menuForm.value.path, // path: menuForm.value.path, //
api: menuForm.value.api , // api: menuForm.value.api, //
icon: menuForm.value.icon, // icon: menuForm.value.icon, //
sort: menuForm.value.sort ?? 1, // 1 sort: menuForm.value.sort ?? 1, // 1
type: menuForm.value.type ?? 1, // type: menuForm.value.type ?? 1, //
} }
await createMenuApi(params) await createMenuApi(params)
await fetchMenuList() await fetchMenuList()
} else { } else {
await editMenuApi(menuForm.value) await editMenuApi(menuForm.value)
console.log('编辑菜单成功', menuForm.value) console.log('编辑菜单成功', menuForm.value)
await fetchMenuList() await fetchMenuList()
}
dialogVisible.value = false
} catch (err) {
console.log('表单验证失败')
return
} }
dialogVisible.value = false
} catch (err) {
console.log('表单验证失败')
return
}
} }
const handleCancel = () => { const handleCancel = () => {
dialogVisible.value = false dialogVisible.value = false
dialogType.value = 'add' dialogType.value = 'add'
menuForm.value = { menuForm.value = {
name: '', // name: '', //
parentName: '', // parentName: '', //
path: '', // path: '', //
api: '', // api: '', //
icon: '', // icon: '', //
sort: null, // 1 sort: null, // 1
type: null, // type: null, //
status: null, // status: null, //
} }
} }
const handleClose = () => { const handleClose = () => {
dialogVisible.value = false dialogVisible.value = false
dialogType.value = 'add' dialogType.value = 'add'
menuForm.value = { menuForm.value = {
name: '', // name: '', //
parentName: '', // parentName: '', //
path: '', // path: '', //
api: '', // api: '', //
icon: '', // icon: '', //
sort: null, // 1 sort: null, // 1
type: null, // type: null, //
status: null, // status: null, //
} }
} }
</script> </script>

View File

@ -1,55 +1,97 @@
<template> <template>
<div> <div>
<!-- 搜索 --> <!-- 搜索 -->
<NoticeSearch :search-form="searchForm" :search-options="searchOptions" @search="handleSearch" <NoticeSearch
@reset="handleReset"> :search-form="searchForm"
<template #button> :search-options="searchOptions"
<el-button type="primary" @click="handleCreate">新建</el-button> @search="handleSearch"
</template> @reset="handleReset"
</NoticeSearch> >
<div class="tab"> <template #button>
<el-button :type="activeTab === 2 ? 'primary' : 'default'" @click="handleTabChange(2)">已发布</el-button> <el-button type="primary" @click="handleCreate" v-auth="'notice_createNotice'"
<el-button :type="activeTab === 1 ? 'primary' : 'default'" @click="handleTabChange(1)">草稿</el-button> >新建</el-button
</div> >
<!-- 表格 --> </template>
<NoticeTable :table-data="noticeTree" :columns="noticeTableColumnsOptions" row-key="id"> </NoticeSearch>
<template #status="{ row }"> <!-- 表格 -->
<!-- 修正状态显示1=草稿2=已发布 --> <NoticeTable :table-data="noticeTree" :columns="noticeTableColumnsOptions" row-key="id">
{{ row.status === 1 ? '草稿' : '已发布' }} <template #status="{ row }">
</template> <!-- 修正状态显示1=草稿2=已发布 -->
<template #action="{ row }"> {{ row.status === 1 ? '草稿' : '已发布' }}
<el-button type="primary" size="small" v-if="row.status === 2" @click="handleView(row)">查看</el-button> </template>
<!-- 草稿才显示编辑/删除已发布仅查看 --> <template #action="{ row }">
<template v-if="row.status === 1"> <el-button type="primary" size="small" v-if="row.status === 2" @click="handleView(row)"
<el-button type="primary" size="small" @click="handleEdit(row)">编辑</el-button> >查看</el-button
<el-button type="danger" size="small" @click="handleDelete(row)">删除</el-button> >
</template> <!-- 草稿才显示编辑/删除已发布仅查看 -->
</template> <template v-if="row.status === 1">
</NoticeTable> <el-button
<!-- 分页 --> type="primary"
<NoticePagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total" size="small"
@pagination-change="handlePaginationChange" /> @click="handleEdit(row)"
<!-- dialog --> v-auth="'notice_editNotice'"
<NoticeDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" >编辑</el-button
:show-footer="dialogType !== 'view'" @confirm="handleSubmit" @cancel="handleCancel" @close="handleClose"> >
<NoticeForm :form-data="noticeForm" :form-rules="noticeRules" :form-items="noticeFormItem" <el-button
:options-map="noticeFormOptionsMap" ref="noticeFormRef" v-if="dialogType !== 'view'"> type="danger"
<template #noticeContent="{ formData }"> size="small"
<div style="border: 1px solid #e6e6e6; margin: 10px 0;"> @click="handleDelete(row)"
<Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" mode="default" v-auth="'notice_delNotice'"
style="border-bottom: 1px solid #ccc;" /> >删除</el-button
<Editor v-model="valueHtml" :defaultConfig="editorConfig" mode="default" >
@onCreated="handleCreated" style="height: 300px; overflow-y: auto;" /> </template>
</div> </template>
</template> </NoticeTable>
</NoticeForm> <!-- 分页 -->
<div v-else class="view-content"> <NoticePagination
<h2>公告名称{{ noticeForm.noticeName }}</h2> v-model="currentPage"
<div v-html="noticeForm.noticeContent"></div> v-model:pageSizeValue="pageSize"
</div> :total="total"
</NoticeDialog> @pagination-change="handlePaginationChange"
<!-- dialog表格 --> />
</div> <!-- dialog -->
<NoticeDialog
:visible="dialogVisible"
:title="dialogTitle"
:type="dialogType"
:show-footer="dialogType !== 'view'"
@confirm="handleSubmit"
@cancel="handleCancel"
@close="handleClose"
>
<NoticeForm
:form-data="noticeForm"
:form-rules="noticeRules"
:form-items="noticeFormItem"
:options-map="noticeFormOptionsMap"
ref="noticeFormRef"
v-if="dialogType !== 'view'"
>
<template #noticeContent="{ formData }">
<div style="border: 1px solid #e6e6e6; margin: 10px 0">
<Toolbar
:editor="editorRef"
:defaultConfig="toolbarConfig"
mode="default"
style="border-bottom: 1px solid #ccc"
/>
<Editor
v-model="valueHtml"
:defaultConfig="editorConfig"
mode="default"
@onCreated="handleCreated"
style="height: 300px; overflow-y: auto"
/>
</div>
</template>
</NoticeForm>
<div v-else class="view-content">
<h2>公告名称{{ noticeForm.noticeName }}</h2>
<div v-html="noticeForm.noticeContent"></div>
</div>
</NoticeDialog>
<!-- dialog表格 -->
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -64,11 +106,14 @@ import { search, tableColumns, formItem } from './options'
import { rules } from './rules' import { rules } from './rules'
// //
import { getNoticeListApi, createNoticeApi, editNoticeApi, deleteNoticeApi } from '@/api/modules/notice' import {
getNoticeListApi,
createNoticeApi,
editNoticeApi,
deleteNoticeApi,
} from '@/api/modules/notice'
import { uploadImageApi } from '@/api/modules/upload' import { uploadImageApi } from '@/api/modules/upload'
import { DomEditor } from '@wangeditor/editor' import { DomEditor } from '@wangeditor/editor'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue' import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import '@wangeditor/editor/dist/css/style.css' import '@wangeditor/editor/dist/css/style.css'
@ -81,48 +126,48 @@ const toolbarRef = shallowRef()
const valueHtml = ref('') const valueHtml = ref('')
// //
const toolbarConfig = { const toolbarConfig = {
excludeKeys: ["group-video", "fullScreen"] excludeKeys: ['group-video', 'fullScreen'],
} }
// //
const editorConfig = { const editorConfig = {
placeholder: '请输入内容...', placeholder: '请输入内容...',
MENU_CONF: { MENU_CONF: {
uploadImage: { uploadImage: {
customUpload: async (file: any, insertFn: any) => { customUpload: async (file: any, insertFn: any) => {
try { try {
// API // API
const res = await uploadImageApi(file); const res = await uploadImageApi(file)
// //
insertFn(res.url); insertFn(res.url)
} catch (error: any) { } catch (error: any) {
// ElNotification // ElNotification
const errMsg = error?.message || error?.data?.msg || '图片上传失败'; const errMsg = error?.message || error?.data?.msg || '图片上传失败'
alert(`图片${file.name}上传失败:${errMsg}`); alert(`图片${file.name}上传失败:${errMsg}`)
ElNotification({ ElNotification({
message: errMsg || '未知的错误!', message: errMsg || '未知的错误!',
type: 'error', type: 'error',
duration: 2000, duration: 2000,
customClass: 'global-notification' customClass: 'global-notification',
}) })
}
},
//
fieldName: 'image',
timeout: 30 * 1000,
maxFileSize: 5 * 1024 * 1024,
allowedFileTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
} }
} },
//
fieldName: 'image',
timeout: 30 * 1000,
maxFileSize: 5 * 1024 * 1024,
allowedFileTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
},
},
} }
/** /**
* @description: 定义搜索数据 * @description: 定义搜索数据
*/ */
const searchForm = ref({ const searchForm = ref({
noticeName: '', noticeName: '',
startDateTime: '', startDateTime: '',
endDateTime: '', endDateTime: '',
status: 2, status: 2,
}) })
const searchOptions = ref(search) const searchOptions = ref(search)
@ -154,19 +199,19 @@ const dialogType = ref('')
* @description: 定义弹窗表格数据 * @description: 定义弹窗表格数据
*/ */
const noticeForm = ref({ const noticeForm = ref({
noticeName: '', noticeName: '',
noticeContent: '', noticeContent: '',
status: 1, status: 1,
id: 0, id: 0,
}) })
const noticeFormRef = ref() const noticeFormRef = ref()
const noticeRules = ref(rules()) const noticeRules = ref(rules())
const noticeFormItem = ref(formItem) const noticeFormItem = ref(formItem)
const noticeFormOptionsMap = ref({ const noticeFormOptionsMap = ref({
status: [ status: [
{ label: '草稿', value: 1 }, { label: '草稿', value: 1 },
{ label: '发布', value: 2 }, { label: '发布', value: 2 },
], ],
}) })
/** /**
@ -174,152 +219,150 @@ const noticeFormOptionsMap = ref({
*/ */
// //
const getNoticeList = async () => { const getNoticeList = async () => {
const params = { const params = {
page: currentPage.value, page: currentPage.value,
page_size: pageSize.value, page_size: pageSize.value,
name: searchForm.value.noticeName, name: searchForm.value.noticeName,
status: searchForm.value.status, status: searchForm.value.status,
publish_start_time: searchForm.value.startDateTime, publish_start_time: searchForm.value.startDateTime,
publish_end_time: searchForm.value.endDateTime, publish_end_time: searchForm.value.endDateTime,
} }
const data: any = await getNoticeListApi(params) const data: any = await getNoticeListApi(params)
// //
noticeTree.value = data.data.map((item: any) => ({ noticeTree.value = data.data.map((item: any) => ({
id: item.id, id: item.id,
noticeName: item.name, noticeName: item.name,
noticeContent: item.content, noticeContent: item.content,
publishTime: item.publish_time, publishTime: item.publish_time,
status: item.status, status: item.status,
})) }))
// //
total.value = data.total total.value = data.total
currentPage.value = data.current_page currentPage.value = data.current_page
pageSize.value = Number(data.per_page) pageSize.value = Number(data.per_page)
} }
/** /**
* @description: 页面加载完成需要请求的数据 * @description: 页面加载完成需要请求的数据
*/ */
onMounted(() => { onMounted(() => {
getNoticeList() getNoticeList()
}) })
/** /**
* @description: 定义搜索方法 * @description: 定义搜索方法
*/ */
const handleSearch = () => { const handleSearch = () => {
getNoticeList() getNoticeList()
} }
const handleReset = () => { const handleReset = () => {
searchForm.value = { searchForm.value = {
noticeName: '', noticeName: '',
startDateTime: '', startDateTime: '',
endDateTime: '', endDateTime: '',
status: 2, status: 2,
} }
activeTab.value = 2 activeTab.value = 2
getNoticeList() getNoticeList()
} }
const handleCreate = () => { const handleCreate = () => {
dialogVisible.value = true dialogVisible.value = true
dialogTitle.value = '新建公告' dialogTitle.value = '新建公告'
dialogType.value = 'create' dialogType.value = 'create'
noticeForm.value = { noticeForm.value = {
noticeName: '', noticeName: '',
noticeContent: '', noticeContent: '',
status: 1, status: 1,
id: 0, id: 0,
} as any } as any
} }
/** /**
* @description: 定义tab方法 * @description: 定义tab方法
*/ */
const handleTabChange = (tab: number) => { const handleTabChange = (tab: number) => {
activeTab.value = tab activeTab.value = tab
searchForm.value.status = tab searchForm.value.status = tab
currentPage.value = 1 currentPage.value = 1
getNoticeList() getNoticeList()
} }
/** /**
* @description: 定义表格方法 * @description: 定义表格方法
*/ */
const handleView = (row: any) => { const handleView = (row: any) => {
console.log(row) console.log(row)
dialogVisible.value = true dialogVisible.value = true
dialogTitle.value = '查看公告' dialogTitle.value = '查看公告'
dialogType.value = 'view' dialogType.value = 'view'
noticeForm.value = { noticeForm.value = {
noticeName: row.noticeName, noticeName: row.noticeName,
noticeContent: row.noticeContent, noticeContent: row.noticeContent,
} as any } as any
} }
const handleEdit = (row: any) => { const handleEdit = (row: any) => {
dialogVisible.value = true dialogVisible.value = true
dialogTitle.value = '编辑公告' dialogTitle.value = '编辑公告'
dialogType.value = 'edit' dialogType.value = 'edit'
noticeForm.value = { noticeForm.value = {
noticeName: row.noticeName, noticeName: row.noticeName,
status: row.status, status: row.status,
id: row.id, id: row.id,
} as any } as any
valueHtml.value = row.noticeContent valueHtml.value = row.noticeContent
} }
// //
const handleDelete = async (row: any) => { const handleDelete = async (row: any) => {
const params = { const params = {
id: row.id, id: row.id,
} }
await deleteNoticeApi(params) await deleteNoticeApi(params)
getNoticeList() getNoticeList()
} }
/** /**
* @description: 定义分页方法 * @description: 定义分页方法
*/ */
const handlePaginationChange = async (page: { currentPage: number, pageSize: number }) => { const handlePaginationChange = async (page: { currentPage: number; pageSize: number }) => {
currentPage.value = page.currentPage currentPage.value = page.currentPage
pageSize.value = page.pageSize pageSize.value = page.pageSize
await getNoticeList() await getNoticeList()
} }
/** /**
* @description: 定义弹窗方法 * @description: 定义弹窗方法
*/ */
const handleSubmit = async () => { const handleSubmit = async () => {
await noticeFormRef.value.validate() await noticeFormRef.value.validate()
if (dialogType.value === 'create') { if (dialogType.value === 'create') {
noticeForm.value.noticeContent = valueHtml.value noticeForm.value.noticeContent = valueHtml.value
const params = { const params = {
name: noticeForm.value.noticeName, name: noticeForm.value.noticeName,
content: noticeForm.value.noticeContent, content: noticeForm.value.noticeContent,
status: noticeForm.value.status, status: noticeForm.value.status,
}
await createNoticeApi(params)
dialogVisible.value = false
getNoticeList()
} else {
noticeForm.value.noticeContent = valueHtml.value
const params = {
id: noticeForm.value.id,
name: noticeForm.value.noticeName,
content: noticeForm.value.noticeContent,
status: noticeForm.value.status,
}
await editNoticeApi(params)
dialogVisible.value = false
getNoticeList()
} }
await createNoticeApi(params)
dialogVisible.value = false
getNoticeList()
} else {
noticeForm.value.noticeContent = valueHtml.value
const params = {
id: noticeForm.value.id,
name: noticeForm.value.noticeName,
content: noticeForm.value.noticeContent,
status: noticeForm.value.status,
}
await editNoticeApi(params)
dialogVisible.value = false
getNoticeList()
}
} }
const handleCancel = () => { const handleCancel = () => {
dialogVisible.value = false dialogVisible.value = false
} }
const handleClose = () => { const handleClose = () => {
dialogVisible.value = false dialogVisible.value = false
} }
/** /**
@ -327,42 +370,42 @@ const handleClose = () => {
*/ */
// //
onBeforeUnmount(() => { onBeforeUnmount(() => {
const editor = editorRef.value const editor = editorRef.value
if (editor == null) return if (editor == null) return
editor.destroy() editor.destroy()
}) })
// //
const handleCreated = (editor: any) => { const handleCreated = (editor: any) => {
editorRef.value = editor editorRef.value = editor
// //
nextTick(() => { nextTick(() => {
const toolbar = DomEditor.getToolbar(editor) const toolbar = DomEditor.getToolbar(editor)
if (toolbar) { if (toolbar) {
const curToolbarConfig = toolbar.getConfig() const curToolbarConfig = toolbar.getConfig()
console.log('curToolbarConfig.toolbarKeys', curToolbarConfig.toolbarKeys) console.log('curToolbarConfig.toolbarKeys', curToolbarConfig.toolbarKeys)
toolbarRef.value = toolbar toolbarRef.value = toolbar
console.log('toolbarRef.value', valueHtml.value) console.log('toolbarRef.value', valueHtml.value)
} }
}) })
} }
</script> </script>
<style scoped> <style scoped>
.tab { .tab {
display: flex; display: flex;
justify-content: center; justify-content: center;
margin-bottom: 10px; margin-bottom: 10px;
.el-button { .el-button {
margin-left: 0px; margin-left: 0px;
} }
} }
.view-content { .view-content {
h2 { h2 {
margin-bottom: 10px; margin-bottom: 10px;
} }
} }
</style> </style>

View File

@ -2,17 +2,39 @@
* @description: * @description:
*/ */
export const search = [ export const search = [
{ prop: 'noticeName', label: '公告名称', type: 'input' as const, placeholder: '请输入公告名称', width: '120px' }, {
{ prop: 'noticeName',
prop: "startDateTime", label: '发布时间', type: 'date' as const, dateType: 'date' as const, // 时间范围选择器 label: '公告名称',
valueFormat: 'YYYY-MM-DD', type: 'input' as const,
width: '180px', placeholder: '请输入公告名称',
}, width: '120px',
{ },
prop: "endDateTime", label: '结束时间', type: 'date' as const, dateType: 'date' as const, // 时间范围选择器 {
valueFormat: 'YYYY-MM-DD', prop: 'startDateTime',
width: '180px', label: '发布时间',
}, type: 'date' as const,
dateType: 'date' as const, // 时间范围选择器
valueFormat: 'YYYY-MM-DD',
width: '180px',
},
{
prop: 'endDateTime',
label: '结束时间',
type: 'date' as const,
dateType: 'date' as const, // 时间范围选择器
valueFormat: 'YYYY-MM-DD',
width: '180px',
},
{
prop: 'status',
label: '状态',
type: 'select' as const,
placeholder: '请选择状态',
options: [
{ label: '草稿', value: 1 },
{ label: '已发布', value: 2 },
],
},
] ]
/** /**

View File

@ -3,7 +3,7 @@
<!-- 角色列表头 --> <!-- 角色列表头 -->
<div class="header"> <div class="header">
<h4>角色列表</h4> <h4>角色列表</h4>
<el-button type="primary" @click="handleAddRole">新增角色</el-button> <el-button type="primary" @click="handleAddRole" v-auth="'roles_create'">新增角色</el-button>
</div> </div>
<!-- 角色列表表格 --> <!-- 角色列表表格 -->
<RoleTable :table-data="roleTree" :columns="roleTableColumns" row-key="id"> <RoleTable :table-data="roleTree" :columns="roleTableColumns" row-key="id">
@ -20,11 +20,33 @@
{{ row.created_at }} {{ row.created_at }}
</template> </template>
<template #action="{ row }"> <template #action="{ row }">
<el-button link type="primary" size="small" @click="handleEditRole(row)"> 编辑 </el-button> <el-button
<el-button link type="primary" size="small" @click="handlePermission(row)"> link
type="primary"
size="small"
@click="handleEditRole(row)"
v-auth="'roles_edit'"
>
编辑
</el-button>
<el-button
link
type="primary"
size="small"
@click="handlePermission(row)"
v-auth="'roles_setPermission'"
>
设置权限 设置权限
</el-button> </el-button>
<el-button link type="danger" size="small" @click="handleDeleteRole(row)"> 删除 </el-button> <el-button
link
type="danger"
size="small"
@click="handleDeleteRole(row)"
v-auth="'roles_del'"
>
删除
</el-button>
</template> </template>
</RoleTable> </RoleTable>
</div> </div>

View File

@ -1,7 +1,9 @@
<template> <template>
<div class="trade-group"> <div class="trade-group">
<el-form-item label="交易组时间设置"> <el-form-item label="交易组时间设置">
<el-button type="primary" @click="addTableItem">添加</el-button> <el-button type="primary" @click="addTableItem" v-auth="'contractVariety_createGroup'"
>添加</el-button
>
</el-form-item> </el-form-item>
<!-- 表格 --> <!-- 表格 -->
<div class="table-box"> <div class="table-box">
@ -60,16 +62,32 @@
</template> </template>
</td> </td>
<td rowspan="2" style="width: 140px"> <td rowspan="2" style="width: 140px">
<el-button type="primary" v-if="!item.editStatus" @click="item.editStatus = true" <el-button
type="primary"
v-if="!item.editStatus"
@click="item.editStatus = true"
v-auth="'contractVariety_editGroup'"
>编辑</el-button >编辑</el-button
> >
<el-button type="danger" v-if="!item.editStatus" @click="deleteGroup(item.id, index)" <el-button
type="danger"
v-if="!item.editStatus"
@click="deleteGroup(item.id, index)"
v-auth="'contractVariety_delGroup'"
>删除</el-button >删除</el-button
> >
<el-button type="primary" v-if="item.editStatus" @click="submitItem(index)" <el-button
type="primary"
v-if="item.editStatus"
@click="submitItem(index)"
v-auth="'contractVariety_editGroup'"
>确认</el-button >确认</el-button
> >
<el-button type="danger" v-if="item.editStatus" @click="item.editStatus = false" <el-button
type="danger"
v-if="item.editStatus"
@click="item.editStatus = false"
v-auth="'contractVariety_editGroup'"
>取消</el-button >取消</el-button
> >
</td> </td>

View File

@ -8,8 +8,17 @@
@reset="handleReset" @reset="handleReset"
> >
<template #button="{ form }"> <template #button="{ form }">
<el-button type="primary" :disabled="!selectedRow" @click="handleEdit()"> 编辑 </el-button> <el-button
<el-button type="primary" @click="handleAdd()"> 添加 </el-button> type="primary"
:disabled="!selectedRow"
@click="handleEdit()"
v-auth="'contracVarietediariety'"
>
编辑
</el-button>
<el-button type="primary" @click="handleAdd()" v-auth="'contractVariety_createVariety'">
添加
</el-button>
</template> </template>
</VarietySearch> </VarietySearch>