Files
medical-mall/pages/mall/admin/marketing/live/anchor.uvue
2026-02-15 16:37:37 +08:00

398 lines
9.2 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="marketing-live-anchor">
<view class="action-bar">
<button class="btn-add" @click="showModal = true">添加主播</button>
</view>
<view class="table-card border-shadow">
<view class="table-container">
<view class="table-head">
<view class="th cell-id">ID</view>
<view class="th cell-name">名称</view>
<view class="th cell-phone">电话</view>
<view class="th cell-wechat">微信号</view>
<view class="th cell-op">操作</view>
</view>
<view class="table-body">
<view v-for="item in anchorList" :key="item.id" class="table-row">
<view class="td cell-id"><text class="td-txt">{{ item.id }}</text></view>
<view class="td cell-name"><text class="td-txt">{{ item.name }}</text></view>
<view class="td cell-phone"><text class="td-txt">{{ item.phone }}</text></view>
<view class="td cell-wechat"><text class="td-txt">{{ item.wechat }}</text></view>
<view class="td cell-op">
<view class="op-links">
<text class="op-link" @click="handleEdit(item)">修改</text>
<text class="op-split">|</text>
<text class="op-link" @click="handleDelete(item)">删除</text>
</view>
</view>
</view>
</view>
</view>
<view class="pagination-footer">
<view class="page-total"><text class="total-txt">共 {{ anchorList.length }} 条</text></view>
<view class="page-select">
<view class="select-mock mini">
<text class="select-val">15条/页</text>
<text class="arrow">▼</text>
</view>
</view>
<view class="page-btns">
<text class="p-btn disabled"></text>
<text class="p-btn active">1</text>
<text class="p-btn disabled"></text>
</view>
</view>
</view>
<!-- Modal Overlay -->
<view v-if="showModal" class="modal-mask" @click="showModal = false"></view>
<!-- Modal Panel -->
<view v-if="showModal" class="modal-panel">
<view class="modal-header">
<text class="modal-title">添加主播</text>
<text class="modal-close" @click="showModal = false">×</text>
</view>
<view class="modal-content">
<view class="form-item">
<text class="form-label required">主播名称:</text>
<input class="form-input" v-model="formData.nickname" placeholder="请输入主播名称" />
</view>
<view class="form-item">
<text class="form-label required">主播微信号:</text>
<input class="form-input" v-model="formData.wechat" placeholder="请输入主播微信号" />
</view>
<view class="form-item">
<text class="form-label required">主播手机号:</text>
<input class="form-input" v-model="formData.phone" placeholder="请输入主播手机号" />
</view>
<view class="form-item">
<text class="form-label">主播图像:</text>
<view class="upload-mock" @click="handleUpload">
<image v-if="formData.avatar_url" :src="formData.avatar_url" mode="aspectFill" class="avatar-preview" />
<text v-else class="upload-ic">🖼️</text>
</view>
</view>
</view>
<view class="modal-footer">
<button class="btn-cancel" @click="showModal = false">取消</button>
<button class="btn-confirm" @click="handleSubmit">确定</button>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref, onMounted } from 'vue'
import { fetchLiveAnchors, saveLiveAnchor, deleteLiveAnchor, LiveAnchor } from '@/services/admin/marketingService.uts'
const showModal = ref(false)
const isLoading = ref(false)
const anchorList = ref<LiveAnchor[]>([])
const formData = ref<LiveAnchor>({
nickname: '',
wechat: '',
phone: '',
avatar_url: '',
status: true
})
onMounted(() => {
loadData()
})
async function loadData() {
isLoading.value = true
try {
const res = await fetchLiveAnchors()
anchorList.value = res
} catch (e) {
uni.showToast({ title: '加载主播失败', icon: 'none' })
} finally {
isLoading.value = false
}
}
function handleAdd() {
formData.value = {
nickname: '',
wechat: '',
phone: '',
avatar_url: '',
status: true
} as LiveAnchor
showModal.value = true
}
function handleEdit(item : LiveAnchor) {
formData.value = { ...item } as LiveAnchor
showModal.value = true
}
async function handleDelete(item : LiveAnchor) {
if (item.id == null) return
uni.showModal({
title: '提示',
content: '确定要删除该主播吗?',
success: async (res) => {
if (res.confirm) {
const success = await deleteLiveAnchor(item.id!)
if (success) {
uni.showToast({ title: '删除成功' })
loadData()
}
}
}
})
}
const handleUpload = () => {
uni.chooseImage({
count: 1,
success: (res) => {
formData.value.avatar_url = res.tempFilePaths[0]
}
})
}
async function handleSubmit() {
if (!formData.value.nickname || !formData.value.phone) {
uni.showToast({ title: '请填写必填项', icon: 'none' })
return
}
try {
const success = await saveLiveAnchor(formData.value)
if (success) {
uni.showToast({ title: '操作成功', icon: 'success' })
showModal.value = false
loadData()
}
} catch (e) {
uni.showToast({ title: '系统异常', icon: 'none' })
}
}
</script>
<style scoped lang="scss">
.marketing-live-anchor {
min-height: 100vh;
background: #f0f2f5;
padding: 16px;
}
.border-shadow {
background: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
}
/* 操作栏 */
.action-bar {
margin-bottom: 16px;
}
.btn-add {
width: auto;
padding: 0 16px;
height: 32px;
background-color: #1890ff;
color: #fff;
font-size: 14px;
border: none;
border-radius: 4px;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
/* 表格区域 */
.table-card {
padding: 24px;
}
.table-head {
display: flex;
flex-direction: row;
background-color: #f8f8f9;
border-bottom: 1px solid #e8eaec;
}
.th {
padding: 12px 8px;
font-size: 13px;
color: #515a6e;
font-weight: bold;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #e8eaec;
align-items: center;
}
.td {
padding: 16px 8px;
}
.td-txt { font-size: 13px; color: #515a6e; }
/* 各列宽度 */
.cell-id { width: 80px; }
.cell-name { flex: 1; min-width: 150px; }
.cell-phone { width: 180px; }
.cell-wechat { width: 180px; }
.cell-op { width: 120px; text-align: right; }
.op-links {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
}
.op-link { color: #1890ff; font-size: 13px; cursor: pointer; }
.op-split { color: #e8eaec; margin: 0 8px; }
/* Pagination */
.pagination-footer {
margin-top: 24px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
gap: 12px;
}
.total-txt { font-size: 13px; color: #606266; }
.select-mock.mini {
width: 100px;
height: 28px;
border: 1px solid #dcdfe6;
border-radius: 4px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 0 12px;
}
.select-val { font-size: 12px; color: #606266; }
.arrow { font-size: 10px; color: #c0c4cc; }
/* Modal Styles */
.modal-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.45);
z-index: 1000;
}
.modal-panel {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 520px;
background-color: #fff;
z-index: 1001;
border-radius: 4px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
display: flex;
flex-direction: column;
}
.modal-header {
padding: 16px 24px;
border-bottom: 1px solid #f0f0f0;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.modal-title {
font-size: 16px;
font-weight: 600;
color: #262626;
}
.modal-close {
font-size: 24px;
color: #bfbfbf;
cursor: pointer;
}
.modal-content {
padding: 24px;
}
.form-item {
margin-bottom: 24px;
}
.form-label {
display: block;
font-size: 14px;
color: #262626;
margin-bottom: 8px;
}
.required::before {
content: '*';
color: #ff4d4f;
margin-right: 4px;
}
.form-input {
width: 100%;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 0 12px;
font-size: 14px;
}
.upload-mock {
width: 80px;
height: 80px;
border: 1px dashed #d9d9d9;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
background-color: #fafafa;
}
.upload-ic { font-size: 24px; color: #bfbfbf; }
.modal-footer {
padding: 10px 16px;
border-top: 1px solid #f0f0f0;
display: flex;
flex-direction: row;
justify-content: flex-end;
gap: 8px;
}
.btn-cancel, .btn-confirm {
width: auto;
padding: 0 15px;
height: 32px;
font-size: 14px;
border-radius: 4px;
cursor: pointer;
}
.btn-cancel {
background-color: #fff;
border: 1px solid #d9d9d9;
color: #595959;
}
.btn-confirm {
background-color: #1890ff;
border: 1px solid #1890ff;
color: #fff;
}
</style>