管理后台修改.
This commit is contained in:
parent
3b5dd2bad6
commit
4b27db42d3
|
|
@ -70,8 +70,8 @@
|
|||
<el-table-column label="用户信息" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<div class="user-info">
|
||||
<span class="nickname">{{ row.user?.nickname || row.user?.realName || '-' }}</span>
|
||||
<span class="phone">{{ row.user?.phone || '-' }}</span>
|
||||
<span class="nickname">{{ row.user?.nickname || '-' }}</span>
|
||||
<span class="uid">UID: {{ row.user?.uid || '-' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
|
@ -137,7 +137,7 @@
|
|||
{{ formatDate(row.createdAt) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="160" fixed="right" align="center">
|
||||
<el-table-column label="操作" width="220" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link size="small" @click="handleViewDetails(row)">
|
||||
<el-icon><View /></el-icon>
|
||||
|
|
@ -147,6 +147,10 @@
|
|||
<el-icon><Edit /></el-icon>
|
||||
状态
|
||||
</el-button>
|
||||
<el-button type="success" link size="small" @click="handleCreatePaymentOrder(row)">
|
||||
<el-icon><Money /></el-icon>
|
||||
收款
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
|
@ -512,19 +516,16 @@
|
|||
<el-descriptions-item label="用户昵称">
|
||||
{{ appointmentDetails.user.nickname || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="真实姓名">
|
||||
{{ appointmentDetails.user.realName || '-' }}
|
||||
<el-descriptions-item label="UID">
|
||||
{{ appointmentDetails.user.uid || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="手机号">
|
||||
{{ appointmentDetails.user.phone || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="WhatsApp">
|
||||
{{ appointmentDetails.user.whatsapp || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="微信号">
|
||||
{{ appointmentDetails.user.wechatId || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="语言偏好">
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
语言偏好
|
||||
<el-tooltip content="用户在小程序中设置的界面语言,联系用户时可参考使用对应语言沟通" placement="top">
|
||||
<el-icon style="margin-left: 4px; cursor: help;"><QuestionFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
{{ getLanguageLabel(appointmentDetails.user.language) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
|
@ -578,6 +579,58 @@
|
|||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- Create Payment Order Dialog -->
|
||||
<el-dialog
|
||||
v-model="paymentDialogVisible"
|
||||
title="创建支付订单"
|
||||
width="500px"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form :model="paymentForm" :rules="paymentRules" ref="paymentFormRef" label-width="100px">
|
||||
<el-form-item label="关联订单">
|
||||
<el-input :value="paymentForm.appointmentNo" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户">
|
||||
<el-input :value="paymentForm.userNickname" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="服务内容" prop="serviceContent">
|
||||
<el-input v-model="paymentForm.serviceContent" placeholder="服务内容描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="金额" prop="amount">
|
||||
<el-input-number
|
||||
v-model="paymentForm.amount"
|
||||
:min="0.01"
|
||||
:precision="2"
|
||||
:step="10"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="支付时间" prop="paymentTime">
|
||||
<el-date-picker
|
||||
v-model="paymentForm.paymentTime"
|
||||
type="datetime"
|
||||
placeholder="选择支付时间"
|
||||
style="width: 100%"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input
|
||||
v-model="paymentForm.notes"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="可选,填写备注信息"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="paymentDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmCreatePaymentOrder" :loading="paymentCreating">
|
||||
创建支付订单
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -633,6 +686,33 @@ const statusOptions = [
|
|||
{ value: 'cancelled', label: '已取消' }
|
||||
]
|
||||
|
||||
// Payment order dialog state
|
||||
const paymentDialogVisible = ref(false)
|
||||
const paymentCreating = ref(false)
|
||||
const paymentFormRef = ref(null)
|
||||
const paymentForm = reactive({
|
||||
appointmentId: '',
|
||||
appointmentNo: '',
|
||||
userId: '',
|
||||
userNickname: '',
|
||||
serviceContent: '',
|
||||
amount: 0,
|
||||
paymentTime: '',
|
||||
notes: ''
|
||||
})
|
||||
const paymentRules = {
|
||||
serviceContent: [
|
||||
{ required: true, message: '请输入服务内容', trigger: 'blur' }
|
||||
],
|
||||
amount: [
|
||||
{ required: true, message: '请输入金额', trigger: 'blur' },
|
||||
{ type: 'number', min: 0.01, message: '金额必须大于0', trigger: 'blur' }
|
||||
],
|
||||
paymentTime: [
|
||||
{ required: true, message: '请选择支付时间', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
// Fetch categories for filter
|
||||
async function fetchCategories() {
|
||||
try {
|
||||
|
|
@ -808,6 +888,51 @@ async function confirmUpdateStatus() {
|
|||
}
|
||||
}
|
||||
|
||||
// Handle create payment order
|
||||
function handleCreatePaymentOrder(row) {
|
||||
paymentForm.appointmentId = row.id
|
||||
paymentForm.appointmentNo = row.appointmentNo
|
||||
paymentForm.userId = row.user?.id || row.userId
|
||||
paymentForm.userNickname = `${row.user?.nickname || '-'} (UID: ${row.user?.uid || '-'})`
|
||||
paymentForm.serviceContent = row.service?.titleZh || row.hotService?.name_zh || row.serviceType || ''
|
||||
paymentForm.amount = parseFloat(row.amount) || 0
|
||||
// 默认设置为当前时间
|
||||
paymentForm.paymentTime = new Date().toISOString().slice(0, 19).replace('T', ' ')
|
||||
paymentForm.notes = ''
|
||||
paymentDialogVisible.value = true
|
||||
}
|
||||
|
||||
// Confirm create payment order
|
||||
async function confirmCreatePaymentOrder() {
|
||||
if (!paymentFormRef.value) return
|
||||
|
||||
await paymentFormRef.value.validate(async (valid) => {
|
||||
if (!valid) return
|
||||
|
||||
paymentCreating.value = true
|
||||
try {
|
||||
const response = await api.post('/api/v1/admin/payment-orders', {
|
||||
userId: paymentForm.userId,
|
||||
appointmentId: paymentForm.appointmentNo,
|
||||
amount: paymentForm.amount,
|
||||
serviceContent: paymentForm.serviceContent,
|
||||
paymentTime: paymentForm.paymentTime,
|
||||
notes: paymentForm.notes
|
||||
})
|
||||
|
||||
if (response.data.success || response.data.code === 0) {
|
||||
ElMessage.success('支付订单创建成功')
|
||||
paymentDialogVisible.value = false
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to create payment order:', error)
|
||||
ElMessage.error(error.response?.data?.message || '创建支付订单失败')
|
||||
} finally {
|
||||
paymentCreating.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
function formatDate(dateStr) {
|
||||
if (!dateStr) return '-'
|
||||
|
|
@ -1018,7 +1143,7 @@ onMounted(() => {
|
|||
color: #303133;
|
||||
}
|
||||
|
||||
.phone {
|
||||
.uid {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@
|
|||
</el-table-column>
|
||||
<el-table-column prop="titleZh" label="中文名称" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="titleEn" label="英文名称" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="titlePt" label="西语名称" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="titleEs" label="西语名称" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="category" label="分类" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.category" size="small">{{ row.category.nameZh || row.category.name_zh || '-' }}</el-tag>
|
||||
|
|
@ -172,8 +172,8 @@
|
|||
<el-form-item label="英文标题" prop="titleEn">
|
||||
<el-input v-model="form.titleEn" placeholder="Please enter English title" />
|
||||
</el-form-item>
|
||||
<el-form-item label="西语标题" prop="titlePt">
|
||||
<el-input v-model="form.titlePt" placeholder="Por favor, ingrese el título en español" />
|
||||
<el-form-item label="西语标题" prop="titleEs">
|
||||
<el-input v-model="form.titleEs" placeholder="Por favor, ingrese el título en español" />
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">其他信息</el-divider>
|
||||
|
|
@ -285,7 +285,7 @@ const defaultForm = {
|
|||
serviceType: '',
|
||||
titleZh: '',
|
||||
titleEn: '',
|
||||
titlePt: '',
|
||||
titleEs: '',
|
||||
image: '',
|
||||
sortOrder: 0,
|
||||
status: 'active'
|
||||
|
|
@ -299,7 +299,7 @@ const formRules = {
|
|||
serviceType: [{ required: true, message: '请选择服务具体类型', trigger: 'change' }],
|
||||
titleZh: [{ required: true, message: '请输入中文标题', trigger: 'blur' }],
|
||||
titleEn: [{ required: true, message: '请输入英文标题', trigger: 'blur' }],
|
||||
titlePt: [{ required: true, message: '请输入西语标题', trigger: 'blur' }]
|
||||
titleEs: [{ required: true, message: '请输入西语标题', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
// 获取服务类型标签
|
||||
|
|
@ -338,11 +338,12 @@ async function fetchServices() {
|
|||
const response = await api.get('/api/v1/admin/services', { params })
|
||||
// Support both old format (success: true) and new format (code: 0)
|
||||
if (response.data.success === true || response.data.code === 0) {
|
||||
services.value = response.data.data
|
||||
// Handle pagination from both formats
|
||||
const paginationData = response.data.pagination || {}
|
||||
pagination.total = paginationData.total || 0
|
||||
pagination.totalPages = paginationData.totalPages || 0
|
||||
services.value = response.data.data || []
|
||||
// Handle pagination from response
|
||||
if (response.data.pagination) {
|
||||
pagination.total = response.data.pagination.total || 0
|
||||
pagination.totalPages = response.data.pagination.totalPages || 0
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch services:', error)
|
||||
|
|
@ -408,7 +409,7 @@ function handleEdit(row) {
|
|||
serviceType: row.serviceType || '',
|
||||
titleZh: row.titleZh,
|
||||
titleEn: row.titleEn,
|
||||
titlePt: row.titlePt,
|
||||
titleEs: row.titleEs,
|
||||
image: row.image || '',
|
||||
sortOrder: row.sortOrder || 0,
|
||||
status: row.status
|
||||
|
|
|
|||
|
|
@ -63,21 +63,6 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="nickname" label="昵称" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="realName" label="真实姓名" min-width="100" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ row.realName || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="phone" label="手机号" min-width="120">
|
||||
<template #default="{ row }">
|
||||
{{ row.phone || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="wechatId" label="微信号" min-width="120">
|
||||
<template #default="{ row }">
|
||||
{{ row.wechatId || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="language" label="语言" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getLanguageTagType(row.language)" size="small">
|
||||
|
|
@ -85,11 +70,6 @@
|
|||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="balance" label="余额" width="100" align="right" sortable="custom">
|
||||
<template #default="{ row }">
|
||||
¥{{ parseFloat(row.balance || 0).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 'active' ? 'success' : 'danger'" size="small">
|
||||
|
|
@ -151,10 +131,6 @@
|
|||
</el-descriptions-item>
|
||||
<el-descriptions-item label="昵称">{{ userDetails.user.nickname || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="UID">{{ userDetails.user.uid || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="真实姓名">{{ userDetails.user.realName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="手机号">{{ userDetails.user.phone || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="WhatsApp">{{ userDetails.user.whatsapp || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="微信号">{{ userDetails.user.wechatId || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="语言偏好">
|
||||
<el-tag :type="getLanguageTagType(userDetails.user.language)" size="small">
|
||||
{{ getLanguageLabel(userDetails.user.language) }}
|
||||
|
|
@ -165,10 +141,6 @@
|
|||
{{ userDetails.user.status === 'active' ? '正常' : '已禁用' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="账户余额">
|
||||
<span class="balance">¥{{ parseFloat(userDetails.user.balance || 0).toFixed(2) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="邀请码">{{ userDetails.user.invitationCode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="邀请人">
|
||||
{{ userDetails.inviter ? (userDetails.inviter.nickname || userDetails.inviter.realName) : '-' }}
|
||||
</el-descriptions-item>
|
||||
|
|
|
|||
|
|
@ -17,13 +17,18 @@ const responseFormatter = (req, res, next) => {
|
|||
|
||||
// Transform old format to new format
|
||||
if (body && typeof body === 'object') {
|
||||
// Success response: { success: true, data: {...} }
|
||||
// Success response: { success: true, data: {...}, pagination: {...} }
|
||||
if (body.success === true) {
|
||||
return originalJson({
|
||||
const response = {
|
||||
code: 0,
|
||||
message: body.message || 'Success',
|
||||
data: body.data || null,
|
||||
});
|
||||
};
|
||||
// Preserve pagination if present
|
||||
if (body.pagination) {
|
||||
response.pagination = body.pagination;
|
||||
}
|
||||
return originalJson(response);
|
||||
}
|
||||
|
||||
// Error response: { success: false, error: {...} }
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ const getAppointmentList = async (options = {}) => {
|
|||
{
|
||||
model: User,
|
||||
as: 'user',
|
||||
attributes: ['id', 'nickname', 'realName', 'phone', 'whatsapp', 'wechatId'],
|
||||
attributes: ['id', 'uid', 'nickname', 'realName', 'phone', 'whatsapp', 'wechatId'],
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -158,7 +158,7 @@ const getAppointmentDetails = async (appointmentId) => {
|
|||
{
|
||||
model: User,
|
||||
as: 'user',
|
||||
attributes: ['id', 'nickname', 'realName', 'phone', 'whatsapp', 'wechatId', 'language', 'balance'],
|
||||
attributes: ['id', 'uid', 'nickname', 'realName', 'phone', 'whatsapp', 'wechatId', 'language', 'balance'],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user