feat:钱包流水增加设备类型

fix:退款bug
This commit is contained in:
dzc committed 2026-10-12 13:43:59 +08:00
1 parent 0b26c6dbce
commit 4c3e46e2c1
6 files changed
+208 -105

No files matched your search

+118 -93
View File
@@ -1,7 +1,7 @@
<template>
<el-select style="width: 100%" v-model="state.selectId" :placeholder="props.placeholder" :multiple="props.multiple"
<el-select style="width: 100%" v-model="selectId" :placeholder="props.placeholder" :multiple="props.multiple"
:collapse-tags="props.multiple" clearable remote-show-suffix filterable remote :remote-method="remoteSearch"
:loading="state.loading" @visible-change="(e: boolean) => e && getOptions()" @change="change">
:loading="state.loading" @visible-change="onVisibleChange" @change="change">
<el-option v-for="v in state.options" :key="v.userId" :label="formatOptionLabel(v)"
:value="getOptionValue(v)" :disabled="isOptionDisabled(v)">
<div class="flex items-center">
@@ -14,7 +14,7 @@
</template>
<script setup lang="ts" name="select-user">
import { reactive } from "vue"
import { reactive, watch } from "vue"
import * as userApi from "/@/api/admin/user"
/** 选项变化事件,单选返回用户对象,多选返回用户对象数组。 */
@@ -48,6 +48,8 @@ export type Props = {
multiple?: boolean
/** 组件绑定值使用用户 ID 还是用户手机号。 */
valueField?: UserValueField
/** 每页加载的用户数量。 */
size?: number
}
/** 用户选择器属性,未声明多选和取值字段时保持原有单选用户 ID 行为。 */
@@ -56,78 +58,98 @@ const props = withDefaults(defineProps<Props>(), {
excludeUserIds: () => [],
multiple: false,
valueField: "userId",
size: 10,
})
/** 远程搜索条件,用户输入关键词后从第一页重新加载。 */
const queryForm = reactive<any>({
nameOrPhone: "",
current: 1,
size: 10,
})
/** 选择器运行状态,选项在远程搜索时保留已选用户以维持标签展示。 */
const state = reactive<any>({
loading: true,
selectId: selectId,
options: [],
loading: false,
options: [] as UserOption[],
})
watch(
selectId,
(newVal, oldValue) => {
if (newVal == oldValue) return
state.selectId = newVal
getOptions()
},
{ immediate: true },
)
/** 请求序号:过期响应不更新选项,loading 只能由最新请求关闭。 */
let requestSeq = 0
/** 关闭 loading 的防抖动定时器,新请求开始时会先取消旧定时器。 */
let loadingTimer: ReturnType<typeof setTimeout> | null = null
/** 最近一次成功加载的列表查询参数;相同参数直接复用已有选项,避免重复调用接口。 */
let lastListQueryKey = ""
/**
* 加载下拉用户;多选模式额外回查已选值,避免远程搜索切换后已选标签退化为原始值。
* 刷新下拉选项。
*
* <p>列表数据只在下拉展开或远程搜索时按需加载,且与最近一次成功参数一致时直接复用已有选项;
* 绑定值变化只回查现有选项中缺失的已选用户。请求带序号保护:
* 过期响应不更新选项,loading 只由最新请求延迟关闭,
* 避免旧请求的关闭动作让仍在进行中的请求看起来已经完成。</p>
*
* @param fetchList 是否需要加载列表选项(下拉展开和远程搜索时为 true)
*/
async function getOptions() {
async function refreshOptions(fetchList: boolean) {
const missingSelectedValues = getMissingSelectedValues()
const listQuery = { current: 1, size: props.size, nameOrPhone: queryForm.nameOrPhone || undefined }
const listQueryKey = JSON.stringify(listQuery)
const needList = fetchList && listQueryKey !== lastListQueryKey
const needEcho = missingSelectedValues.length > 0
// 列表已是最新且已选用户都能命中现有选项时无需任何请求,也不再闪一次 loading。
if (!needList && !needEcho) {
return
}
const seq = ++requestSeq
if (loadingTimer) {
clearTimeout(loadingTimer)
loadingTimer = null
}
// 只要有请求进行中就保持 loading,由最新完成的请求统一延迟关闭。
state.loading = true
try {
state.loading = true
if (props.multiple) {
await getMultipleOptions()
const [listOptions, echoOptions] = await Promise.all([
needList ? fetchUserPage(listQuery) : Promise.resolve([] as UserOption[]),
needEcho ? loadSelectedOptions(missingSelectedValues) : Promise.resolve([] as UserOption[]),
])
// 等待期间又有新请求进入时丢弃本次响应,避免旧结果覆盖新搜索。
if (seq !== requestSeq) {
return
}
const _pa = {
...queryForm,
userId: props.valueField === "userId" ? selectId.value : undefined,
nameOrPhone: props.valueField === "phone" && selectId.value ? selectId.value : queryForm.nameOrPhone,
if (needList) {
lastListQueryKey = listQueryKey
// 实时读取当前已选值,避免请求期间选择变化导致已选选项被新列表挤出。
const selectedValueSet = new Set(getSelectedValues())
const keepOptions = (state.options as UserOption[]).filter((item) =>
selectedValueSet.has(String(getOptionValue(item))),
)
state.options = mergeOptions([...keepOptions, ...echoOptions, ...listOptions])
} else {
// 未重新拉取列表时保留现有选项,仅合并回查到的已选用户。
state.options = mergeOptions([...(state.options as UserOption[]), ...echoOptions])
}
const { data } = await userApi.pageSelect(_pa)
state.options = data.records ?? []
} catch (error) {
// 请求失败时保留现有选项;loading 由 finally 统一关闭,不中断组件交互。
console.error("加载用户下拉选项失败", error)
} finally {
setTimeout(() => (state.loading = false), 500)
// 只有最新请求允许关闭 loading;保留 500ms 延迟避免下拉数据闪动,新请求会先取消本定时器。
if (seq === requestSeq) {
loadingTimer = setTimeout(() => {
state.loading = false
loadingTimer = null
}, 500)
}
}
}
/**
* 加载多选搜索结果和已选用户;手机号模式按每个历史手机号精确回显对应用户。
*/
async function getMultipleOptions() {
const selectedValues = getSelectedValues()
const selectedValueSet = new Set(selectedValues)
const cachedSelectedOptions = (state.options as UserOption[]).filter((item) =>
selectedValueSet.has(String(getOptionValue(item))),
)
const cachedValueSet = new Set(cachedSelectedOptions.map((item) => String(getOptionValue(item))))
const missingSelectedValues = selectedValues.filter((value) => !cachedValueSet.has(value))
const [searchResponse, selectedOptions] = await Promise.all([
userApi.pageSelect({ ...queryForm }),
loadSelectedOptions(missingSelectedValues),
])
state.options = mergeOptions([
...cachedSelectedOptions,
...selectedOptions,
...(searchResponse.data?.records ?? []),
])
/** 调用用户选择分页接口并把返回记录规范为选项数组。 */
async function fetchUserPage(query: Record<string, unknown>): Promise<UserOption[]> {
const { data } = await userApi.pageSelect(query)
return (data?.records ?? []) as UserOption[]
}
/**
* 回查尚未缓存的已选用户,用户 ID 使用批量条件,手机号使用关键词查询后精确匹配。
* 回查尚未缓存到选项列表的已选用户,用户 ID 使用批量条件,手机号使用关键词查询后精确匹配。
*
* @param selectedValues 尚未加载到选项列表的绑定值
* @returns 与绑定值精确对应的用户选项
@@ -135,21 +157,28 @@ async function getMultipleOptions() {
async function loadSelectedOptions(selectedValues: string[]): Promise<UserOption[]> {
if (!selectedValues.length) return []
if (props.valueField === "userId") {
const { data } = await userApi.pageSelect({
return fetchUserPage({
userIds: selectedValues,
current: 1,
size: Math.max(queryForm.size, selectedValues.length),
size: Math.max(props.size, selectedValues.length),
})
return data.records ?? []
}
const responses = await Promise.all(
selectedValues.map((phone) => userApi.pageSelect({ current: 1, size: 10, nameOrPhone: phone })),
selectedValues.map((phone) => fetchUserPage({ current: 1, size: 10, nameOrPhone: phone })),
)
return responses
.flatMap(({ data }) => data.records ?? [])
.flatMap((records) => records)
.filter((item: UserOption) => selectedValues.includes(String(item.phone || "")))
}
/** 计算尚未加载到选项列表的绑定值,用于按需回查已选用户。 */
function getMissingSelectedValues(): string[] {
const selectedValues = getSelectedValues()
if (!selectedValues.length) return []
const knownValueSet = new Set((state.options as UserOption[]).map((item) => String(getOptionValue(item))))
return selectedValues.filter((value) => !knownValueSet.has(value))
}
/**
* 按用户 ID 合并远程搜索和已选用户,防止相同用户在下拉中重复展示。
*
@@ -166,49 +195,48 @@ function mergeOptions(options: UserOption[]): UserOption[] {
})
}
/**
* 将当前绑定值规范为字符串数组,供多选回显和选项匹配使用。
*
* @returns 去除空值后的已选用户 ID 或手机号集合
*/
/** 处理远程搜索关键词变化,避免相同关键词重复请求。 */
function remoteSearch(params: string) {
if (params != queryForm.nameOrPhone) {
queryForm.nameOrPhone = params
refreshOptions(true)
}
}
/** 下拉展开时按需加载列表选项;收起时不处理。 */
function onVisibleChange(visible: boolean) {
if (visible) {
refreshOptions(true)
}
}
/** 绑定值变化时只补齐缺失的已选用户回显,不重新拉取列表。 */
watch(
selectId,
(newVal, oldValue) => {
if (newVal == oldValue) return
refreshOptions(false)
},
{ immediate: true },
)
/** 将当前绑定值规范为字符串数组,供多选回显和选项匹配使用。 */
function getSelectedValues(): string[] {
const values = Array.isArray(selectId.value) ? selectId.value : [selectId.value]
return values.filter((value) => value !== undefined && value !== null && value !== "").map(String)
}
/**
* 按当前取值模式返回选项绑定值。
*
* @param user 用户下拉选项
* @returns 用户 ID 或手机号;手机号缺失时返回空字符串并禁用该选项
*/
/** 按当前取值模式返回选项绑定值。 */
function getOptionValue(user: UserOption): string | number {
return props.valueField === "phone" ? user.phone || "" : user.userId
}
/**
* 生成用户选项标签,手机号取值模式会明确提示用户尚未填写手机号。
*
* @param user 用户下拉选项
* @returns 姓名及可用手机号组成的展示文本
*/
/** 生成选项标签,手机号取值模式会明确提示用户尚未填写手机号。 */
function formatOptionLabel(user: UserOption): string {
if (user.phone) return `${user.name}-${user.phone}`
return props.valueField === "phone" ? `${user.name}-未填写手机号` : user.name
}
/**
* 处理远程搜索关键词变化,避免相同关键词重复请求。
*
* @param params 姓名、手机号或用户名关键词
*/
function remoteSearch(params: string) {
if (params != queryForm.nameOrPhone) {
queryForm.nameOrPhone = params
getOptions()
}
}
/**
* 向调用方返回完整用户选项;多选返回选项数组,单选保持原有单个用户对象行为。
*
@@ -217,23 +245,20 @@ function remoteSearch(params: string) {
function change(v: any) {
if (props.multiple) {
const selectedValueSet = new Set((Array.isArray(v) ? v : []).map(String))
const targetUsers = state.options.filter((item: UserOption) =>
const targetUsers = (state.options as UserOption[]).filter((item) =>
selectedValueSet.has(String(getOptionValue(item))),
)
emit("change", targetUsers)
return
}
const targetUser = state.options.find((item: UserOption) => String(getOptionValue(item)) === String(v))
const targetUser = (state.options as UserOption[]).find(
(item) => String(getOptionValue(item)) === String(v),
)
emit("change", targetUser)
}
/**
* 判断用户是否不可选;手机号取值模式下未填写手机号的用户不能作为监护人值提交。
*
* @param user 用户下拉选项
* @returns 是否禁用当前选项
*/
function isOptionDisabled(user: UserOption) {
/** 判断是否不可选;手机号取值模式下未填写手机号的用户不能作为监护人值提交。 */
function isOptionDisabled(user: UserOption): boolean {
const isExcluded = props.excludeUserIds.some((id) => String(id) === String(user.userId))
return isExcluded || (props.valueField === "phone" && !user.phone)
}
+18 -8
View File
@@ -26,14 +26,18 @@
start-placeholder="开始时间" end-placeholder="结束时间" :clearable="false" />
</el-col>
<el-col :span="12" class="mb-3">
<div class="add-bar__label mb-1">可预约日期</div>
<div class="flex w-full flex-wrap items-center gap-2">
<el-date-picker v-model="addBar.dateRange" type="daterange" class="min-w-0 flex-1"
value-format="YYYY-MM-DD" start-placeholder="开始日期" end-placeholder="结束日期" />
<el-tooltip placement="top" content="用户在这些日期到店用餐;每个日期一天一行,添加后还可在表格中单独调整餐段">
<div class="add-bar__label mb-1 cursor-help">可预约日期(?)</div>
</el-tooltip>
<el-date-picker v-model="addBar.dateRange" type="daterange" class="w-full"
value-format="YYYY-MM-DD" start-placeholder="用餐开始日期" end-placeholder="用餐结束日期" />
<div class="mt-2 flex w-full flex-nowrap items-center gap-2 whitespace-nowrap">
<span class="text-xs text-[var(--el-text-color-secondary)]">快捷生成:结束预约次日起,连续</span>
<el-input-number v-model="addBar.pushDays" :min="1" :max="31" size="small"
controls-position="right" class="w-24" aria-label="向后推天数" />
<el-tooltip content="以结束预约时间的次日起,连续生成 N 天可预约日期" placement="top">
<el-button size="small" icon="MagicStick" @click="generateDateRange">生成</el-button>
controls-position="right" class="w-20" aria-label="向后推天数" />
<span class="text-xs text-[var(--el-text-color-secondary)]">天</span>
<el-tooltip content="按左侧开放预约时间的结束时间次日起算,自动填入上方的日期范围" placement="top">
<el-button size="small" icon="MagicStick" @click="generateDateRange">生成日期</el-button>
</el-tooltip>
</div>
</el-col>
@@ -292,7 +296,13 @@ const generateDateRange = () => {
}
const days = Math.max(1, Math.min(MAX_DAYS, Number(addBar.pushDays) || 1))
const start = dayjs(endTime).startOf("day").add(1, "day")
addBar.dateRange = [start.format("YYYY-MM-DD"), start.add(days - 1, "day").format("YYYY-MM-DD")]
const end = start.add(days - 1, "day")
addBar.dateRange = [start.format("YYYY-MM-DD"), end.format("YYYY-MM-DD")]
useMessage().success(
days > 1
? `已生成 ${start.format("M月D日")} ~ ${end.format("M月D日")} 共 ${days} 天可预约日期`
: `已生成 ${start.format("M月D日")} 1 天可预约日期`,
)
}
/**
@@ -0,0 +1,19 @@
import { useDict } from "/@/hooks/dict"
/** 设备类型筛选白名单:仅保留钱包流水实际消费的设备(咖啡机/食堂/云版消费机)。 */
const SYS_TYPE_FILTER_VALUES = ["13", "2", "9"]
/**
* 钱包流水页共用的设备类型筛选项:按白名单过滤 hard_type 字典。
*
* 设备类型仅对订单支付/退款流水有意义(hard_type 固化列),消费、退款等
* 订单类流水页面统一复用本选项;充值、记账、退还类流水 hard_type 恒空,不提供该筛选。
*/
export function useSysTypeOptions() {
const { hard_type } = useDict("hard_type")
/** 设备类型筛选项:按白名单过滤后的字典项。 */
const sysTypeOptions = computed(() =>
(hard_type.value || []).filter((item: any) => SYS_TYPE_FILTER_VALUES.includes(item.value))
)
return { sysTypeOptions }
}
+17 -1
View File
@@ -18,6 +18,13 @@
<el-form-item style="width: 300px" label="用户" prop="userId">
<select-user v-model="state.queryForm.userId" />
</el-form-item>
<el-form-item style="width: 260px" label="设备类型" prop="sysTypes">
<el-select v-model="state.queryForm.sysTypes" multiple clearable filterable
collapse-tags collapse-tags-tooltip placeholder="全部">
<el-option v-for="option in sysTypeOptions" :key="option.value"
:label="option.label" :value="option.value" />
</el-select>
</el-form-item>
<el-form-item style="width: 300px" label="退款时间" prop="createTimeList">
<el-date-picker v-model="state.queryForm.createTimeList" value-format="YYYY-MM-DD" type="daterange" />
</el-form-item>
@@ -62,6 +69,11 @@
<dict-tag :options="wallet_biz" :value="row.bizType" />
</template>
</el-table-column>
<el-table-column prop="sysTypeName" width="120" label="设备类型">
<template #default="{ row }">
{{ row.sysTypeName || "-" }}
</template>
</el-table-column>
<el-table-column prop="price" align="center" width="200" label="退款金额">
<template #default="{ row }">
<div class="color-success">{{ displayPrice(row.price) }}</div>
@@ -123,13 +135,14 @@
<script setup lang="ts" name="systemWalletRecharge">
import { BasicTableProps, useTable } from "/@/hooks/table"
import { pageTk, tkTotal, submitTkExport } from "/@/api/wallet/wallet-transaction"
import { useMessage, useMessageBox } from "/@/hooks/message"
import { useMessage } from "/@/hooks/message"
import { useDict } from "/@/hooks/dict"
import * as hardAfterSaleApi from "/@/api/hard/hard-after-sale"
import { displayPrice } from "/@/utils/formatMoney"
import AsyncTaskProgressComponent from "/@/components/AsyncTaskProgress.vue"
import { createEmptyUserIdentityStudentQuery, createEmptyUserIdentityTeacherQuery } from "/@/components/UserIdentityQuery/types"
import { useWalletUserExport } from "../composables/useWalletUserExport"
import { useSysTypeOptions } from "../composables/useSysTypeOptions"
const showDrawer = ref(false)
const balanceStatisticsLoading = ref(false)
@@ -145,6 +158,8 @@ const TransactionRemarkForm = defineAsyncComponent(() => import("../components/t
const TransactionInfoForm = defineAsyncComponent(() => import("../components/transaction-info-form.vue"))
// 定义查询字典
const { wallet_type, wallet_biz } = useDict("wallet_type", "wallet_biz")
/** 设备类型筛选项:白名单过滤后的 hard_type 字典(咖啡机/食堂/云版消费机)。 */
const { sysTypeOptions } = useSysTypeOptions()
// 定义变量内容
const formDialogRef = ref()
/** 通用流水备注编辑弹窗引用。 */
@@ -167,6 +182,7 @@ const state: BasicTableProps = reactive<BasicTableProps>({
studentInfo: createEmptyUserIdentityStudentQuery(),
teacherInfo: createEmptyUserIdentityTeacherQuery(),
hasFace: "",
sysTypes: [],
},
pageList: pageTk,
})
+20 -2
View File
@@ -20,6 +20,13 @@
:label="option.label" :value="option.value" />
</el-select>
</el-form-item>
<el-form-item style="width: 260px" label="设备类型" prop="sysTypes">
<el-select v-model="state.queryForm.sysTypes" multiple clearable filterable
collapse-tags collapse-tags-tooltip placeholder="全部">
<el-option v-for="option in sysTypeOptions" :key="option.value"
:label="option.label" :value="option.value" />
</el-select>
</el-form-item>
<el-form-item style="width: 300px" label="流水时间" prop="createTimeList">
<el-date-picker v-model="state.queryForm.createTimeList" value-format="YYYY-MM-DD"
type="daterange" start-placeholder="开始日期" end-placeholder="结束日期" />
@@ -74,6 +81,11 @@
<dict-tag :options="wallet_biz" :value="row.bizType" />
</template>
</el-table-column>
<el-table-column prop="sysTypeName" width="120" label="设备类型">
<template #default="{ row }">
{{ row.sysTypeName || "-" }}
</template>
</el-table-column>
<el-table-column prop="title" label="流水标题" min-width="180" show-overflow-tooltip />
<el-table-column prop="price" width="160" align="center" label="交易金额">
<template #default="{ row }">
@@ -97,9 +109,11 @@
v-auths="['wallet_walletTransaction_edit', 'wallet_walletTransaction_update_info']">
<template #default="{ row }">
<div class="row-actions">
<el-button icon="EditPen" link type="primary" v-auth="'wallet_walletTransaction_edit'"
<el-button icon="EditPen" link type="primary"
v-auth="'wallet_walletTransaction_edit'"
@click="remarkFormRef?.openDialog(row)">修改备注</el-button>
<el-button icon="Edit" link type="primary" v-auth="'wallet_walletTransaction_update_info'"
<el-button icon="Edit" link type="primary"
v-auth="'wallet_walletTransaction_update_info'"
@click="infoFormRef?.openDialog(row)">修改信息</el-button>
</div>
</template>
@@ -128,6 +142,7 @@ import { displayPrice } from "/@/utils/formatMoney"
import AsyncTaskProgressComponent from "/@/components/AsyncTaskProgress.vue"
import { createEmptyUserIdentityStudentQuery, createEmptyUserIdentityTeacherQuery } from "/@/components/UserIdentityQuery/types"
import { useWalletUserExport } from "../composables/useWalletUserExport"
import { useSysTypeOptions } from "../composables/useSysTypeOptions"
/** 所有钱包流水列表共用的备注编辑表单。 */
const TransactionRemarkForm = defineAsyncComponent(() => import("../components/transaction-remark-form.vue"))
@@ -142,6 +157,8 @@ const transactionTypeOptions = [
/** 页面展示交易类型、钱包类型和操作标签所需的字典数据。 */
const { wallet_type, wallet_biz, wallet_transaction_tag } = useDict("wallet_type", "wallet_biz", "wallet_transaction_tag")
/** 设备类型筛选项:白名单过滤后的 hard_type 字典(咖啡机/食堂/云版消费机)。 */
const { sysTypeOptions } = useSysTypeOptions()
/** 搜索面板引用,用于执行 Element Plus 表单重置。 */
const queryRef = ref()
/** 部门树引用,用于重置已选部门和包含模式。 */
@@ -161,6 +178,7 @@ const state: BasicTableProps = reactive<BasicTableProps>({
teacherInfo: createEmptyUserIdentityTeacherQuery(),
hasFace: "",
tagList: [],
sysTypes: [],
},
pageList: pageAll,
})
+16 -1
View File
@@ -21,6 +21,13 @@
<el-form-item style="width: 200px" label="充值方式" prop="bizType">
<dict-select v-model="state.queryForm.bizType" dict-code="wallet_biz" />
</el-form-item>
<el-form-item style="width: 260px" label="设备类型" prop="sysTypes">
<el-select v-model="state.queryForm.sysTypes" multiple clearable filterable
collapse-tags collapse-tags-tooltip placeholder="全部">
<el-option v-for="option in sysTypeOptions" :key="option.value"
:label="option.label" :value="option.value" />
</el-select>
</el-form-item>
<el-form-item style="width: 300px" label="消费时间" prop="createTimeList">
<el-date-picker v-model="state.queryForm.createTimeList" value-format="YYYY-MM-DD" type="daterange" />
</el-form-item>
@@ -65,6 +72,11 @@
<dict-tag :options="wallet_biz" :value="row.bizType" />
</template>
</el-table-column>
<el-table-column prop="sysTypeName" width="120" label="设备类型">
<template #default="{ row }">
{{ row.sysTypeName || "-" }}
</template>
</el-table-column>
<el-table-column prop="price" align="center" width="200" label="消费金额">
<template #default="{ row }">
<div class="color-danger">{{ displayPrice(row.price) }}</div>
@@ -132,10 +144,10 @@ import { pageXf, xfTotal, submitXfExport } from "/@/api/wallet/wallet-transactio
import { useMessage, useMessageBox } from "/@/hooks/message"
import { useDict } from "/@/hooks/dict"
import { displayPrice } from "/@/utils/formatMoney"
import { deptTree } from "/@/api/admin/dept"
import AsyncTaskProgressComponent from "/@/components/AsyncTaskProgress.vue"
import { createEmptyUserIdentityStudentQuery, createEmptyUserIdentityTeacherQuery } from "/@/components/UserIdentityQuery/types"
import { useWalletUserExport } from "../composables/useWalletUserExport"
import { useSysTypeOptions } from "../composables/useSysTypeOptions"
const { push } = useRouter()
@@ -154,6 +166,8 @@ const TransactionRemarkForm = defineAsyncComponent(() => import("../components/t
const TransactionInfoForm = defineAsyncComponent(() => import("../components/transaction-info-form.vue"))
// 定义查询字典
const { wallet_type, wallet_biz } = useDict("wallet_type", "wallet_biz")
/** 设备类型筛选项:白名单过滤后的 hard_type 字典(咖啡机/食堂/云版消费机)。 */
const { sysTypeOptions } = useSysTypeOptions()
// 定义变量内容
const formDialogRef = ref()
/** 通用流水备注编辑弹窗引用。 */
@@ -174,6 +188,7 @@ const state: BasicTableProps = reactive<BasicTableProps>({
studentInfo: createEmptyUserIdentityStudentQuery(),
teacherInfo: createEmptyUserIdentityTeacherQuery(),
hasFace: "",
sysTypes: [],
},
pageList: pageXf,
})