Commit fd981482 authored by daniel's avatar daniel

fix(markNoRule): 新增号码簿规则

parent 6d7b4aa4
...@@ -513,7 +513,8 @@ ...@@ -513,7 +513,8 @@
}, '删除') }, '删除')
]) ])
} }
}] }
]
const pagination = ref({ pageSize: 20, page: 1 }) const pagination = ref({ pageSize: 20, page: 1 })
const keyword = ref('') const keyword = ref('')
// 文件列表 // 文件列表
......
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="fns" uri="/WEB-INF/tlds/fns.tld" %>
<%@ taglib prefix="fnc" uri="/WEB-INF/tlds/fnc.tld" %>
<c:set var="ctx" value="${pageContext.request.contextPath}${fns:getAdminPath()}" />
<c:set var="path" value="${pageContext.request.contextPath}${fns:getPath()}" />
<c:set var="jaaproot" value="${pageContext.request.contextPath}" />
<c:set var="ctxStatic" value="${pageContext.request.contextPath}/static" />
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>后台管理系统</title>
<!-- ... 保留原有的 Vue 引入 ... -->
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<!-- 添加 Naive UI 的引入 -->
<!-- 会使用最新版本,你最好指定一个版本 -->
<script src="https://unpkg.com/naive-ui"></script>
<!-- <script nomodule
src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"></script> -->
<script type="module"
src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"></script>
<script nomodule
src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"></script>
<!-- Favicon-->
<link rel="shortcut icon" href="https://img.geexek.com/files/home/favicon.ico">
<!-- 自定义css -->
<link rel="stylesheet" href="${ctxStatic}/css/base.css">
<!-- Bootstrap CSS-->
<link rel="stylesheet"
href="${ctxStatic}/newdermon/vendor/bootstrap/css/bootstrap.min.css">
<!-- theme stylesheet-->
<link rel="stylesheet"
href="${ctxStatic}/newdermon/css/style.default.css?v=<%=System.currentTimeMillis()%>"
id="theme-stylesheet">
<!-- Custom stylesheet - for your changes-->
<link rel="stylesheet"
href="${ctxStatic}/newdermon/css/custom.css?v=<%=System.currentTimeMillis()%>">
<!-- Favicon-->
<link rel="shortcut icon" href="https://img.geexek.com/files/home/favicon.ico">
<link rel="stylesheet"
href="${ctxStatic}/cmpt/setting.css?v=<%=System.currentTimeMillis()%>">
<script src="${ctxStatic}/newdermon/vendor/jquery/jquery.min.js"></script>
<style>
#app {
padding: 0 20px 20px 20px;
}
.layout {
min-height: 100vh;
}
.carousel-img {
width: 100%;
height: 240px;
object-fit: cover;
}
[v-cloak] {
display: none;
}
.n-message .n-message__icon>* {
position: absolute;
left: 0;
top: -5px !important;
right: 0;
bottom: 0;
}
.n-dialog__icon>* {
position: absolute;
left: 0;
top: -2px !important;
right: 0;
bottom: 0;
}
.light-green {
height: 108px;
background-color: rgba(0, 128, 0, 0.12);
}
.green {
height: 108px;
background-color: rgba(0, 128, 0, 0.24);
}
</style>
</head>
<body>
<jsp:include page="../common/header.jsp"></jsp:include>
<div class="page-content d-flex align-items-stretch main-body">
<jsp:include page="../common/menu.jsp"></jsp:include>
<div id="app" class="flex-1" v-cloak>
<n-config-provider :locale="locale" :date-locale="dateLocale">
<n-modal-provider>
<n-message-provider>
<n-notification-provider>
<my-app></my-app>
</n-notification-provider>
</n-message-provider>
</n-modal-provider>
</n-config-provider>
</div>
</div>
<script type="text/x-template" id="my-app">
<n-space vertical >
<!-- 搜索框 -->
<div class="w-full flex flex-row items-start gap-x-3" >
<div class="bg-white flex flex-col gap-y-2 p-3" style="min-width:250px;">
<h4 class="flex flex-row items-center justify-between w-full gap-x-2">
规则列表
<n-button type="primary" size="small" @click="onAddMarkNoRule" >
新增
</n-button>
</h4>
<div v-for="(item,index) in markNoRules" :key="item.value" class="flex rounded-sm cursor-pointer flex-row items-center gap-x-1">
<span class="py-1 px-4 rounded-sm animate-fade-in" :class="[ruleIndex===index?'bg-primary text-white':'']" class="flex-1" @click="changeMarkNoRule(index)"> {{item.description}}
</span>
</div>
</div>
<div class="flex-1 flex flex-col gap-y-2">
<n-card title="参赛号展示效果" :bordered="false">
<n-grid x-gap="12" :cols="markNoRuleValue.details.length">
<n-gi v-for="(item,index) in markNoRuleValue.details" :key="index">
<span class="w-full text-2xl font-bold flex items-center justify-center" :class="[index%2===0?'light-green':'green']" v-if="item.type ===RuleTypeEnum.DIGIT.value">
6
</span>
<span class="w-full text-2xl font-bold flex items-center justify-center" :class="[index%2===0?'light-green':'green']" v-if="item.type ===RuleTypeEnum.LETTER.value">
A
</span>
<span class="w-full text-2xl font-bold flex items-center justify-center" :class="[index%2===0?'light-green':'green']" v-if="item.type ===RuleTypeEnum.ANY.value">
B
</span>
<span class="w-full text-2xl font-bold flex items-center justify-center" :class="[index%2===0?'light-green':'green']" v-if="item.type ===RuleTypeEnum.FIXED.value">
{{item.fixedValue}}
</span>
</n-gi>
</n-grid>
</n-card>
<!-- 顶部的搜索框 -->
<n-card title="规则信息" :bordered="false">
<n-form
ref="markNoRuleRef"
:model="markNoRuleValue"
:rules="markNoRuleRules"
label-placement="left"
label-width="auto"
size="small"
>
<n-form-item label="规则描述" path="description">
<n-input v-model:value="markNoRuleValue.description" placeholder="请输入规则描述" />
</n-form-item>
<n-form-item label="排除字符" >
<n-input v-model:value="markNoRuleValue.excludedChars" placeholder="请输入全局排查的字符集" />
</n-form-item>
<n-form-item label="对应性别" path="gender">
<n-select v-model:value="markNoRuleValue.gender" :options="GenderEnum.data.map(v=>({label:v.name,value:v.value}))" />
</n-form-item>
<n-form-item label="组别名称" >
<div class="flex flex-col w-full">
<n-input v-model:value="markNoRuleValue.roadName" placeholder="请输入组别名(选填)" />
<span class="text-red">填了之后,导入名单里面如果存在组别名,就会根据组别名匹配</span>
</div>
</n-form-item>
<n-form-item label="参赛号长度" path="totalLength">
<n-input-number :min="1"
@update:value="changeTotalLength"
:max="10" v-model:value="markNoRuleValue.totalLength" placeholder="请输入参赛号长度" />
</n-form-item>
</n-form>
</n-card>
<n-card title="号段规则" :bordered="false">
<n-data-table size="small" :columns="detailsColumns" :loading="loading" :data="markNoRuleValue.details" :pagination="false"
:bordered="false"/>
</n-card>
<n-card title="枪次规则" :bordered="false">
<template #header-extra>
<n-button type='primary' :loading="loading" @click="addWave">新增</n-button>
</template>
<n-data-table size="small" :columns="waveColumns" :loading="loading" :data="markNoRuleValue.waves" :pagination="false"
:bordered="false"/>
</n-card>
<div class="flex flex-row items-center gap-x-2 w-full">
<div class="flex-1">
<n-button :loading="loading" :block="true" @click="submit" type="primary">保存规则</n-button>
</div>
<div class="flex-1" v-if="markNoRuleValue.id">
<n-button :loading="loading" :block="true" @click="showDeleteModal=true" type="error">删除规则</n-button>
</div>
</div>
</div>
</div>
<n-modal
v-model:show="showDeleteModal"
preset="dialog"
title="确认"
content="你确定要删除该规则吗?"
positive-text="确认"
negative-text="算了"
@positive-click="deleteMarkNoRule"
/>
</n-space>
</script>
<script>
// 枚举class
class SuperEnum {
constructor(param) {
const keyObj = {};
const valueObj = {};
const nameObj = {};
if (!Array.isArray(param)) {
throw new TypeError('param is not an array!');
}
param.forEach((element) => {
if (!element.key || !element.name) {
return;
}
// 使用对象存储键值对,方便快速查找
keyObj[element.key] = element;
valueObj[element.value] = element;
nameObj[element.name] = element;
// 动态添加属性
this[element.key] = element;
this[element.name] = element;
if (element.key !== element.value) {
this[element.value] = element;
}
});
// 保存源数组
this.data = param;
this.keyObj = keyObj;
this.valueObj = valueObj;
this.nameObj = nameObj;
// 防止被修改
Object.freeze(this);
}
getColorByKey(key) {
const prop = this.keyOf(key);
if (!prop) {
const error = new Error('No enum constant');
error.code = 'ENUM_NOT_FOUND';
throw error;
}
return prop.color;
}
getData() {
return this.data;
}
getKeyByName(name) {
const prop = this.nameOf(name);
if (!prop) {
const error = new Error('No enum constant');
error.code = 'ENUM_NOT_FOUND';
throw error;
}
return prop.key;
}
getNameByKey(key) {
const prop = this.keyOf(key);
if (!prop) {
const error = new Error('No enum constant');
error.code = 'ENUM_NOT_FOUND';
throw error;
}
return prop.name;
}
getNameByValue(value) {
const prop = this.valueOf(value);
if (!prop) {
const error = new Error('No enum constant');
error.code = 'ENUM_NOT_FOUND';
throw error;
}
return prop.name;
}
getValueByKey(key) {
const prop = this.keyOf(key);
if (!prop) {
const error = new Error('No enum constant');
error.code = 'ENUM_NOT_FOUND';
throw error;
}
return prop.value;
}
getValueByName(name) {
const prop = this.nameOf(name);
if (!prop) {
const error = new Error('No enum constant');
error.code = 'ENUM_NOT_FOUND';
throw error;
}
return prop.value;
}
keyOf(key) {
return this.keyObj[key];
}
nameOf(name) {
return this.nameObj[name];
}
valueOf(value) {
return this.valueObj[value];
}
}
// to 方法,封装try catch
const to = async (
promise,
errorExt,
) => {
try {
const data = await promise
const result = [null, data]
return result
} catch (error) {
if (errorExt) {
const parsedError = Object.assign({}, error, errorExt)
return [parsedError, undefined]
}
return [error, undefined]
}
}
// 选号规则类型
const RuleTypeEnum =
new SuperEnum([
{ key: 'DIGIT', name: '数字', color: 'warning', value: 'DIGIT' },
{ key: 'LETTER', name: '字母', color: 'primary', value: 'LETTER' },
{ key: 'ANY', name: '任意', color: 'success', value: 'ANY' },
{ key: 'FIXED', name: '固定值', color: 'info', value: 'FIXED' },
])
// 性别枚举 0 男 1 女 -1 不限
const GenderEnum =
new SuperEnum([
{ key: 'BOY', name: '男', color: 'warning', value: 0 },
{ key: 'GIRL', name: '女', color: 'primary', value: 1 },
{ key: 'ALL', name: '不限', color: 'success', value: -1 },
])
</script>
<script>
const { createApp, defineComponent, reactive, ref, onMounted, h } = Vue;
const { useMessage, useNotification, dateZhCN, NButton, NTag, zhCN, NSpace, NImageGroup, NImage, NText, NInputNumber, NSelect, NInput, NTimePicker } = naive
// 添加全局 loading 指令
const ECC_BASE_URL = "https://ecc.geexek.com"
const SAVE_OR_UPDATE = ECC_BASE_URL + "/api-pick/v1/save-or-update"
const RULE_LIST = ECC_BASE_URL + "/api-pick/v1/rule-list?"
const DELETE_RULE = ECC_BASE_URL + "/api-pick/v1/delete-rule?"
const MyApp = defineComponent({
name: 'MyApp',
template: '#my-app',
setup() {
const message = useMessage()
const notification = useNotification()
const loading = ref(false)
/***---------------- 号码簿规则 start -----------------*/
const markNoRuleRef = ref(null)
const markNoRules = ref([])
// 规则初始化
const initialValue = {
id: undefined,
totalLength: 3, // 参赛号长度
gender: GenderEnum.ALL.value, // 参赛号长度
description: '',// 描述
excludedChars: "",// 全局排除的字符集
roadName: "", // 组别名
details: [
{
id: undefined,
type: RuleTypeEnum.DIGIT.value,
position: 1,
fixedValue: ''
},
{
id: undefined,
type: RuleTypeEnum.DIGIT.value,
position: 2,
fixedValue: ''
},
{
id: undefined,
type: RuleTypeEnum.DIGIT.value,
position: 3,
fixedValue: ''
},
],// 选号规则详情
waves: [] // 枪次
}
// 选号规则
const markNoRuleValue = ref(initialValue)
// 当前操作的规则索引
const ruleIndex = ref(0)
// 删除确认弹窗
const showDeleteModal = ref(false)
// 号码簿规则校验
const markNoRuleRules = {
description: {
required: true,
message: '请输入规则名称',
trigger: 'blur'
},
totalLength: {
required: true,
message: '请输入选号长度',
trigger: 'blur',
type: 'number',
}
}
// 分类切换,刷新table
const changeMarkNoRule = (index) => {
ruleIndex.value = index
markNoRuleValue.value = markNoRules.value[index]
}
// 获取分类列表
const getMarkNoRules = async () => {
const params = {
cmptId: getCId()
}
loading.value = true;
// 发起请求
const [err, res] = await to(fetch(RULE_LIST + new URLSearchParams(params).toString(), {
method: "GET",
headers: {
'Content-Type': 'application/json'
}
}).then(res => res.json()))
loading.value = false
if (err) {
message.error(err.message)
}
if (res.code === 200) {
markNoRules.value = res.data
if (res.data.length) {
changeMarkNoRule(ruleIndex.value === -1 ? 0 : ruleIndex.value)
} else {
markNoRuleValue.value = initialValue
}
} else {
message.error(res.msg)
}
}
// 提交
const submit = (e) => {
e.preventDefault()
markNoRuleRef.value?.validate(async (errors) => {
if (errors) return message.error('请检查表单是否填写完整')
// 使用fetch发起请求
const params = {
...markNoRuleValue.value,
gtCmptId: getCId(),
}
loading.value = true
// 判断 waves里面的waveNumber是否存在重复,重复的话提示
let waveNumberSet = new Set()
markNoRuleValue.value.waves.forEach(item => {
waveNumberSet.add(item.waveNumber)
})
if (waveNumberSet.size !== markNoRuleValue.value.waves.length) {
message.error('枪次不能重复')
return
}
const messageReactive = message.loading(markNoRuleValue.value.id ? '规则更新中...' : '规则添加中...', {
duration: 0
})
// 发起请求
const [err, data] = await to(fetch(SAVE_OR_UPDATE, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
}).then(res => res.json()))
loading.value = false
messageReactive.destroy()
if (data.code === 200) {
message.success('操作成功')
getMarkNoRules()
} else {
message.error(data.message)
}
})
}
const detailsColumns = [
{
title: 'ID',
key: 'id',
},
{
title: '位置',
key: 'position',
align: 'center'
},
{
title: '类型',
key: 'type',
align: 'center',
render(row, index) {
return h(NSelect, {
value: row.type,
options: RuleTypeEnum.data.map(v => ({ label: v.name, value: v.value })),
onUpdateValue(v) {
markNoRuleValue.value.details[index].type = v
}
})
}
}, {
title: '固定值',
key: 'fixedValue',
align: 'center',
width: 200,
render(row, index) {
return h(NInput, {
value: row.fixedValue,
maxlength: 1,
disabled: row.type !== RuleTypeEnum.FIXED.value,
onUpdateValue(v) {
markNoRuleValue.value.details[index].fixedValue = v
}
})
}
},
]
// 选号长度改变
const changeTotalLength = (v) => {
// details 不做清空,需要对原来的数据保留
if (markNoRuleValue.value.details.length < v) {
for (let i = 0; i < v - markNoRuleValue.value.details.length; i++) {
markNoRuleValue.value.details.push({
id: undefined,
type: RuleTypeEnum.DIGIT.value,
position: markNoRuleValue.value.details.length + i + 1,
fixedValue: ''
})
}
}
// 如果是减少,需要对原来的数据进行删除
if (markNoRuleValue.value.details.length > v) {
markNoRuleValue.value.details.splice(v)
}
}
// 新增规则
const onAddMarkNoRule = () => {
markNoRuleValue.value = initialValue
ruleIndex = -1
}
// 删除规则
const deleteMarkNoRule = async () => {
loading.value = true
const messageReactive = message.loading('删除中...', {
duration: 0
})
// 发起请求
const [err, data] = await to(fetch(DELETE_RULE + 'ruleId=' + markNoRuleValue.value.id, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
}).then(res => res.json()))
loading.value = false
messageReactive.destroy()
if (data.code === 200) {
message.success('操作成功')
getMarkNoRules()
} else {
message.error(data.message)
}
}
/***---------------- 号码簿规则 end-----------------*/
/***---------------- 枪次表 start-----------------*/
const waveColumns = [
{
title: 'ID',
key: 'id',
width: 50,
},
{
title: '枪次',
key: 'waveNumber',
align: 'center',
render(row, index) {
return h(NInputNumber, {
value: row.waveNumber,
min: 1,
max: 100,
onUpdateValue(v) {
data.value[index].waveNumber = v
}
})
}
},
{
title: '最低分',
key: 'scoreFrom',
align: 'center',
render(row, index) {
return h(NTimePicker, {
formattedValue: row.scoreFrom,
valueFormat: 'HH:mm:ss',
onUpdateValue(_, formatValue) {
markNoRuleValue.value.waves[index].scoreFrom = formatValue
}
})
}
},
{
title: '最高分',
key: 'scoreTo',
align: 'center',
render(row, index) {
return h(NTimePicker, {
formattedValue: row.scoreTo,
valueFormat: 'HH:mm:ss',
onUpdateValue(_, formatValue) {
markNoRuleValue.value.waves[index].scoreTo = formatValue
}
})
}
},
{
title: '操作',
key: 'actions',
width: 100,
align: 'center',
render: (row, index) => {
return h(NButton, {
type: 'error',
size: 'small',
onClick: () => {
markNoRuleValue.value.waves.splice(index, 1)
}
}, '删除')
}
}
]
// 新增枪次
const addWave = () => {
let index = markNoRuleValue.value.waves.length
markNoRuleValue.value.waves.push({
id: undefined,
waveNumber: index + 1,
scoreFrom: undefined,
scoreTo: undefined,
})
}
/***---------------- 枪次表 end-----------------*/
onMounted(() => {
getMarkNoRules()
})
return {
// 枪次表相关
waveColumns,
addWave,
// 参赛号规则
markNoRules,
ruleIndex,
detailsColumns,
changeMarkNoRule,
markNoRuleValue,
markNoRuleRef,
markNoRuleRules,
submit,
changeTotalLength,
onAddMarkNoRule,
deleteMarkNoRule,
showDeleteModal,
// 公共数据
loading,
RuleTypeEnum,
GenderEnum
}
}
})
const app = Vue.createApp({
setup() {
return {
locale: zhCN,
dateLocale: dateZhCN
}
}
})
app.component('my-app', MyApp)
app.use(naive)
app.mount('#app')
</script>
</body>
</html>
\ No newline at end of file
...@@ -362,6 +362,13 @@ ...@@ -362,6 +362,13 @@
<span class="menu-txt">发票管理</span> <span class="menu-txt">发票管理</span>
</a> </a>
</li> </li>
<li class="markNoRule menu-sub-item">
<a
href="/gk/markNoRule/index/${cmpt.cmptId}?uId=${uId}&cId=${cId}&timestamp=${timestamp}&sign=${sign}">
<span class="left-arrow"></span>
<span class="menu-txt">选号规则</span>
</a>
</li>
<li class="markNoSelect menu-sub-item"> <li class="markNoSelect menu-sub-item">
<a <a
href="/gk/markNoSelect/index/${cmpt.cmptId}?uId=${uId}&cId=${cId}&timestamp=${timestamp}&sign=${sign}"> href="/gk/markNoSelect/index/${cmpt.cmptId}?uId=${uId}&cId=${cId}&timestamp=${timestamp}&sign=${sign}">
...@@ -438,7 +445,7 @@ ...@@ -438,7 +445,7 @@
var classes = ['cmptSetting', 'nameList', 'manual', 'contact', 'manage', var classes = ['cmptSetting', 'nameList', 'manual', 'contact', 'manage',
'route', 'road', 'equip', 'photo', 'news', 'cert', 'data', 'route', 'road', 'equip', 'photo', 'news', 'cert', 'data',
'banner', 'score', 'auto', 'scan', 'review', 'customer', 'rule', 'banner', 'score', 'auto', 'scan', 'review', 'customer', 'rule',
'outter-link', 'sure-letter', 'rankSetting', 'service', "module", "swiper", "bag", "invoice", "markNoSelect", "feedback"]; 'outter-link', 'sure-letter', 'rankSetting', 'service', "module", "swiper", "bag", "invoice", "markNoSelect", "feedback", "markNoRule"];
let url = document.location.href; let url = document.location.href;
var done = false; var done = false;
for (var i in classes) { for (var i in classes) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment