[色子游戏]玩家-优化样式
This commit is contained in:
@@ -61,5 +61,15 @@ export default {
|
||||
url: '/dice/player/DicePlayer/destroy',
|
||||
data: params
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 仅更新状态(列表内开关用)
|
||||
*/
|
||||
updateStatus(params: { id: number | string; status: number }) {
|
||||
return request.put<any>({
|
||||
url: '/dice/player/DicePlayer/updateStatus',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,19 @@
|
||||
@pagination:size-change="handleSizeChange"
|
||||
@pagination:current-change="handleCurrentChange"
|
||||
>
|
||||
<!-- 状态:开关直接修改 -->
|
||||
<template #status="{ row }">
|
||||
<ElSwitch
|
||||
v-permission="'dice:player:index:update'"
|
||||
:model-value="row.status === 1"
|
||||
:loading="row._statusLoading"
|
||||
@change="(v: boolean) => handleStatusChange(row, v ? 1 : 0)"
|
||||
/>
|
||||
</template>
|
||||
<!-- 平台币:tag 展示 -->
|
||||
<template #coin="{ row }">
|
||||
<ElTag type="info" size="small">{{ row.coin ?? 0 }}</ElTag>
|
||||
</template>
|
||||
<!-- 操作列 -->
|
||||
<template #operation="{ row }">
|
||||
<div class="flex gap-2">
|
||||
@@ -77,11 +90,13 @@
|
||||
import TableSearch from './modules/table-search.vue'
|
||||
import EditDialog from './modules/edit-dialog.vue'
|
||||
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = ref({
|
||||
username: undefined,
|
||||
name: undefined,
|
||||
status: undefined,
|
||||
coin: undefined,
|
||||
is_up: undefined
|
||||
})
|
||||
|
||||
// 搜索处理
|
||||
@@ -90,6 +105,14 @@
|
||||
getData()
|
||||
}
|
||||
|
||||
// 权重列带 % 的 formatter
|
||||
const weightFormatter = (_row: any, _column: any, cellValue: unknown) =>
|
||||
cellValue != null && cellValue !== '' ? `${cellValue}%` : '-'
|
||||
|
||||
// 倍率列展示:0=正常 1=强制杀猪 2=T1高倍率
|
||||
const isUpFormatter = (_row: any, _column: any, cellValue: unknown) =>
|
||||
cellValue === 0 ? '正常' : cellValue === 1 ? '强制杀猪' : cellValue === 2 ? 'T1高倍率' : '-'
|
||||
|
||||
// 表格配置
|
||||
const {
|
||||
columns,
|
||||
@@ -109,27 +132,39 @@
|
||||
apiFn: api.list,
|
||||
columnsFactory: () => [
|
||||
{ type: 'selection' },
|
||||
{ prop: 'username', label: '用户名' },
|
||||
{ prop: 'name', label: '昵称' },
|
||||
{ prop: 'password', label: '密码' },
|
||||
{ prop: 'status', label: '状态' },
|
||||
{ prop: 'coin', label: '平台币' },
|
||||
{ prop: 'is_up', label: '倍率' },
|
||||
{ prop: 't1_wight', label: 'T1池权重' },
|
||||
{ prop: 't2_wight', label: 'T2池权重' },
|
||||
{ prop: 't3_wight', label: 'T3池权重' },
|
||||
{ prop: 't4_wight', label: 'T4池权重' },
|
||||
{ prop: 't5_wight', label: 'T5池权重' },
|
||||
{ prop: 'status', label: '状态', width: 88, useSlot: true },
|
||||
{ prop: 'coin', label: '平台币', width: 100, useSlot: true },
|
||||
{ prop: 'is_up', label: '倍率', width: 80, formatter: isUpFormatter },
|
||||
{ prop: 't1_wight', label: 'T1池权重', width: 100, formatter: weightFormatter },
|
||||
{ prop: 't2_wight', label: 'T2池权重', width: 100, formatter: weightFormatter },
|
||||
{ prop: 't3_wight', label: 'T3池权重', width: 100, formatter: weightFormatter },
|
||||
{ prop: 't4_wight', label: 'T4池权重', width: 100, formatter: weightFormatter },
|
||||
{ prop: 't5_wight', label: 'T5池权重', width: 100, formatter: weightFormatter },
|
||||
{ prop: 'total_draw_count', label: '总抽奖次数' },
|
||||
{ prop: 'paid_draw_count', label: '购买抽奖次数' },
|
||||
{ prop: 'free_draw_count', label: '赠送抽奖次数' },
|
||||
{ prop: 'created_at', label: '创建时间' },
|
||||
{ prop: 'updated_at', label: '更新时间' },
|
||||
{ prop: 'deleted_at', label: '删除时间' },
|
||||
{ prop: 'operation', label: '操作', width: 100, fixed: 'right', useSlot: true }
|
||||
{ prop: 'operation', label: '操作', width: 120, fixed: 'right', useSlot: true }
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
// 状态开关切换(列表内直接修改)
|
||||
const handleStatusChange = async (row: Record<string, any>, status: number) => {
|
||||
row._statusLoading = true
|
||||
try {
|
||||
await api.updateStatus({ id: row.id, status })
|
||||
row.status = status
|
||||
} catch {
|
||||
refreshData()
|
||||
} finally {
|
||||
row._statusLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑配置
|
||||
const {
|
||||
dialogType,
|
||||
@@ -141,5 +176,4 @@
|
||||
handleSelectionChange,
|
||||
selectedRows
|
||||
} = useSaiAdmin()
|
||||
|
||||
</script>
|
||||
|
||||
@@ -14,32 +14,56 @@
|
||||
<el-form-item label="昵称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入昵称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="密码" prop="password">
|
||||
<el-input v-model="formData.password" type="password" placeholder="请输入密码" show-password />
|
||||
<el-form-item label="密码" prop="password" :rules="passwordRules">
|
||||
<el-input
|
||||
v-model="formData.password"
|
||||
type="password"
|
||||
placeholder="编辑留空则不修改"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<sa-switch v-model="formData.status" />
|
||||
</el-form-item>
|
||||
<el-form-item label="平台币" prop="coin">
|
||||
<el-input-number v-model="formData.coin" placeholder="请输入平台币" />
|
||||
<el-input-number
|
||||
v-model="formData.coin"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:disabled="dialogType === 'add'"
|
||||
placeholder="创建时默认0,不可改"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="倍率" prop="is_up">
|
||||
<el-select v-model="formData.is_up" :options="[]" placeholder="请选择倍率" clearable />
|
||||
<el-select v-model="formData.is_up" placeholder="请选择倍率" clearable style="width: 100%">
|
||||
<el-option label="正常" :value="0" />
|
||||
<el-option label="强制杀猪" :value="1" />
|
||||
<el-option label="T1高倍率" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="T1池权重" prop="t1_wight">
|
||||
<el-slider v-model="formData.t1_wight" placeholder="请输入T1池权重" />
|
||||
<el-form-item label="T1池权重(%)" prop="t1_wight">
|
||||
<el-slider v-model="formData.t1_wight" :min="0" :max="100" :step="0.01" show-input />
|
||||
</el-form-item>
|
||||
<el-form-item label="T2池权重" prop="t2_wight">
|
||||
<el-slider v-model="formData.t2_wight" placeholder="请输入T2池权重" />
|
||||
<el-form-item label="T2池权重(%)" prop="t2_wight">
|
||||
<el-slider v-model="formData.t2_wight" :min="0" :max="100" :step="0.01" show-input />
|
||||
</el-form-item>
|
||||
<el-form-item label="T3池权重" prop="t3_wight">
|
||||
<el-slider v-model="formData.t3_wight" placeholder="请输入T3池权重" />
|
||||
<el-form-item label="T3池权重(%)" prop="t3_wight">
|
||||
<el-slider v-model="formData.t3_wight" :min="0" :max="100" :step="0.01" show-input />
|
||||
</el-form-item>
|
||||
<el-form-item label="T4池权重" prop="t4_wight">
|
||||
<el-slider v-model="formData.t4_wight" placeholder="请输入T4池权重" />
|
||||
<el-form-item label="T4池权重(%)" prop="t4_wight">
|
||||
<el-slider v-model="formData.t4_wight" :min="0" :max="100" :step="0.01" show-input />
|
||||
</el-form-item>
|
||||
<el-form-item label="T5池权重" prop="t5_wight">
|
||||
<el-slider v-model="formData.t5_wight" placeholder="请输入T5池权重" />
|
||||
<el-form-item label="T5池权重(%)" prop="t5_wight">
|
||||
<el-slider v-model="formData.t5_wight" :min="0" :max="100" :step="0.01" show-input />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div class="text-gray-500 text-sm">
|
||||
五个池权重总和:<span :class="Math.abs(weightsSum - 100) > 0.01 ? 'text-red-500' : ''">{{
|
||||
weightsSum
|
||||
}}</span
|
||||
>% / 100%(必须为100%)
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -75,106 +99,112 @@
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
/**
|
||||
* 弹窗显示状态双向绑定
|
||||
*/
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => emit('update:modelValue', value)
|
||||
})
|
||||
|
||||
/**
|
||||
* 表单验证规则
|
||||
*/
|
||||
const WEIGHT_KEYS = ['t1_wight', 't2_wight', 't3_wight', 't4_wight', 't5_wight'] as const
|
||||
const weightsSum = computed(() => {
|
||||
return WEIGHT_KEYS.reduce((sum, key) => sum + Number(formData[key] ?? 0), 0)
|
||||
})
|
||||
|
||||
/** 新增时密码必填,编辑时选填 */
|
||||
const passwordRules = computed(() =>
|
||||
props.dialogType === 'add' ? [{ required: true, message: '密码必需填写', trigger: 'blur' }] : []
|
||||
)
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
username: [{ required: true, message: '用户名必需填写', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '昵称必需填写', trigger: 'blur' }],
|
||||
password: [{ required: true, message: '密码必需填写', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '状态必需填写', trigger: 'blur' }],
|
||||
coin: [{ required: true, message: '平台币必需填写', trigger: 'blur' }],
|
||||
coin: [{ required: true, message: '平台币必需填写', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
/**
|
||||
* 初始数据
|
||||
*/
|
||||
const initialFormData = {
|
||||
id: null,
|
||||
id: null as number | null,
|
||||
username: '',
|
||||
name: '',
|
||||
password: '',
|
||||
status: 1,
|
||||
coin: '0.00',
|
||||
is_up: null,
|
||||
t1_wight: '',
|
||||
t2_wight: '',
|
||||
t3_wight: '',
|
||||
t4_wight: '',
|
||||
t5_wight: '',
|
||||
status: 1 as number,
|
||||
coin: 0 as number,
|
||||
is_up: null as number | null,
|
||||
t1_wight: 0 as number,
|
||||
t2_wight: 0 as number,
|
||||
t3_wight: 0 as number,
|
||||
t4_wight: 0 as number,
|
||||
t5_wight: 0 as number
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
const formData = reactive({ ...initialFormData })
|
||||
|
||||
/**
|
||||
* 监听弹窗打开,初始化表单数据
|
||||
*/
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
initPage()
|
||||
}
|
||||
if (newVal) initPage()
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* 初始化页面数据
|
||||
*/
|
||||
const initPage = async () => {
|
||||
// 先重置为初始值
|
||||
Object.assign(formData, initialFormData)
|
||||
// 如果有数据,则填充数据
|
||||
if (props.data) {
|
||||
await nextTick()
|
||||
initForm()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化表单数据
|
||||
*/
|
||||
const numKeys = [
|
||||
'id',
|
||||
'status',
|
||||
'coin',
|
||||
'is_up',
|
||||
't1_wight',
|
||||
't2_wight',
|
||||
't3_wight',
|
||||
't4_wight',
|
||||
't5_wight'
|
||||
]
|
||||
|
||||
const initForm = () => {
|
||||
if (props.data) {
|
||||
for (const key in formData) {
|
||||
if (props.data[key] != null && props.data[key] != undefined) {
|
||||
;(formData as any)[key] = props.data[key]
|
||||
if (!props.data) return
|
||||
for (const key of Object.keys(formData)) {
|
||||
if (!(key in props.data)) continue
|
||||
if (key === 'password') {
|
||||
;(formData as any).password = ''
|
||||
continue
|
||||
}
|
||||
const val = props.data[key]
|
||||
if (numKeys.includes(key)) {
|
||||
;(formData as any)[key] =
|
||||
key === 'id' ? (val != null ? Number(val) || null : null) : Number(val) || 0
|
||||
} else {
|
||||
;(formData as any)[key] = val ?? ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭弹窗并重置表单
|
||||
*/
|
||||
const handleClose = () => {
|
||||
visible.value = false
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交表单
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
if (Math.abs(weightsSum.value - 100) > 0.01) {
|
||||
ElMessage.warning('五个池权重总和必须为100%')
|
||||
return
|
||||
}
|
||||
const payload = { ...formData }
|
||||
if (props.dialogType === 'edit' && !payload.password) {
|
||||
delete (payload as any).password
|
||||
}
|
||||
if (props.dialogType === 'add') {
|
||||
await api.save(formData)
|
||||
await api.save(payload)
|
||||
ElMessage.success('新增成功')
|
||||
} else {
|
||||
await api.update(formData)
|
||||
await api.update(payload)
|
||||
ElMessage.success('修改成功')
|
||||
}
|
||||
emit('success')
|
||||
|
||||
@@ -18,6 +18,35 @@
|
||||
<el-input v-model="formData.name" placeholder="请输入昵称" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-bind="setSpan(6)">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="formData.status" placeholder="全部" clearable style="width: 100%">
|
||||
<el-option label="启用" :value="1" />
|
||||
<el-option label="禁用" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-bind="setSpan(6)">
|
||||
<el-form-item label="平台币" prop="coin">
|
||||
<el-input-number
|
||||
v-model="formData.coin"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
placeholder="精确搜索"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-bind="setSpan(6)">
|
||||
<el-form-item label="倍率" prop="is_up">
|
||||
<el-select v-model="formData.is_up" placeholder="全部" clearable style="width: 100%">
|
||||
<el-option label="正常" :value="0" />
|
||||
<el-option label="强制杀猪" :value="1" />
|
||||
<el-option label="T1高倍率" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</sa-search-bar>
|
||||
</template>
|
||||
|
||||
@@ -32,41 +61,33 @@
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emits>()
|
||||
// 展开/收起
|
||||
const isExpanded = ref<boolean>(false)
|
||||
|
||||
// 表单数据双向绑定
|
||||
const searchBarRef = ref()
|
||||
const formData = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit('update:modelValue', val)
|
||||
})
|
||||
|
||||
// 重置
|
||||
function handleReset() {
|
||||
searchBarRef.value?.ref.resetFields()
|
||||
emit('reset')
|
||||
}
|
||||
|
||||
// 搜索
|
||||
async function handleSearch() {
|
||||
emit('search', formData.value)
|
||||
}
|
||||
|
||||
// 展开/收起
|
||||
function handleExpand(expanded: boolean) {
|
||||
isExpanded.value = expanded
|
||||
}
|
||||
|
||||
// 栅格占据的列数
|
||||
const setSpan = (span: number) => {
|
||||
return {
|
||||
span: span,
|
||||
xs: 24, // 手机:满宽显示
|
||||
sm: span >= 12 ? span : 12, // 平板:大于等于12保持,否则用半宽
|
||||
md: span >= 8 ? span : 8, // 中等屏幕:大于等于8保持,否则用三分之一宽
|
||||
const setSpan = (span: number) => ({
|
||||
span,
|
||||
xs: 24,
|
||||
sm: span >= 12 ? span : 12,
|
||||
md: span >= 8 ? span : 8,
|
||||
lg: span,
|
||||
xl: span
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -39,6 +39,9 @@ class DicePlayerController extends BaseController
|
||||
$where = $request->more([
|
||||
['username', ''],
|
||||
['name', ''],
|
||||
['status', ''],
|
||||
['coin', ''],
|
||||
['is_up', ''],
|
||||
]);
|
||||
$query = $this->logic->search($where);
|
||||
$data = $this->logic->getList($query);
|
||||
@@ -99,6 +102,26 @@ class DicePlayerController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅更新状态(列表内开关用)
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('大富翁-玩家修改', 'dice:player:index:update')]
|
||||
public function updateStatus(Request $request): Response
|
||||
{
|
||||
$id = $request->input('id');
|
||||
$status = $request->input('status');
|
||||
if ($id === null || $id === '') {
|
||||
return $this->fail('缺少 id');
|
||||
}
|
||||
if ($status === null || $status === '') {
|
||||
return $this->fail('缺少 status');
|
||||
}
|
||||
$this->logic->edit($id, ['status' => (int) $status]);
|
||||
return $this->success('修改成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
|
||||
@@ -16,6 +16,9 @@ use app\dice\model\player\DicePlayer;
|
||||
*/
|
||||
class DicePlayerLogic extends BaseLogic
|
||||
{
|
||||
/** 密码加密盐(可与 config 统一) */
|
||||
private const PASSWORD_SALT = 'dice_player_salt_2024';
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
@@ -24,4 +27,35 @@ class DicePlayerLogic extends BaseLogic
|
||||
$this->model = new DicePlayer();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加数据(密码 md5+salt 加密)
|
||||
*/
|
||||
public function add(array $data): mixed
|
||||
{
|
||||
if (!empty($data['password'])) {
|
||||
$data['password'] = $this->hashPassword($data['password']);
|
||||
}
|
||||
return parent::add($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改数据(仅当传入 password 时用 md5+salt 加密后更新)
|
||||
*/
|
||||
public function edit($id, array $data): mixed
|
||||
{
|
||||
if (isset($data['password']) && $data['password'] !== '') {
|
||||
$data['password'] = $this->hashPassword($data['password']);
|
||||
} else {
|
||||
unset($data['password']);
|
||||
}
|
||||
return parent::edit($id, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 密码加密:md5(salt . password)
|
||||
*/
|
||||
private function hashPassword(string $password): string
|
||||
{
|
||||
return md5(self::PASSWORD_SALT . $password);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,4 +62,34 @@ class DicePlayer extends BaseModel
|
||||
$query->where('name', 'like', '%'.$value.'%');
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态 搜索
|
||||
*/
|
||||
public function searchStatusAttr($query, $value)
|
||||
{
|
||||
if ($value !== '' && $value !== null) {
|
||||
$query->where('status', '=', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台币 搜索
|
||||
*/
|
||||
public function searchCoinAttr($query, $value)
|
||||
{
|
||||
if ($value !== '' && $value !== null) {
|
||||
$query->where('coin', '=', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 倍率 搜索
|
||||
*/
|
||||
public function searchIs_upAttr($query, $value)
|
||||
{
|
||||
if ($value !== '' && $value !== null) {
|
||||
$query->where('is_up', '=', $value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class DicePlayerValidate extends BaseValidate
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
* 定义场景(update 时密码可选,不填则不修改)
|
||||
*/
|
||||
protected $scene = [
|
||||
'save' => [
|
||||
@@ -49,7 +49,6 @@ class DicePlayerValidate extends BaseValidate
|
||||
'update' => [
|
||||
'username',
|
||||
'name',
|
||||
'password',
|
||||
'status',
|
||||
'coin',
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user