Merge branch 'develop' of gitee.com:youlaiorg/vue-uniapp-template into develop
This commit is contained in:
180
src/api/system/dict.ts
Normal file
180
src/api/system/dict.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
const DICT_BASE_URL = "/api/v1/dict";
|
||||
|
||||
const DictAPI = {
|
||||
/**
|
||||
* 获取字典分页列表
|
||||
*
|
||||
* @param queryParams 查询参数
|
||||
* @returns 字典分页结果
|
||||
*/
|
||||
getPage(queryParams: DictPageQuery) {
|
||||
return request<DictPageVO[]>({
|
||||
url: `${DICT_BASE_URL}/page`,
|
||||
method: "GET",
|
||||
data: queryParams,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取字典表单数据
|
||||
*
|
||||
* @param id 字典ID
|
||||
* @returns 字典表单数据
|
||||
*/
|
||||
getFormData(id: number) {
|
||||
return request<DictForm>({
|
||||
url: `${DICT_BASE_URL}/${id}/form`,
|
||||
method: "GET",
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增字典
|
||||
*
|
||||
* @param data 字典表单数据
|
||||
*/
|
||||
add(data: DictForm) {
|
||||
return request({
|
||||
url: `${DICT_BASE_URL}`,
|
||||
method: "POST",
|
||||
data: data,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改字典
|
||||
*
|
||||
* @param id 字典ID
|
||||
* @param data 字典表单数据
|
||||
*/
|
||||
update(id: number, data: DictForm) {
|
||||
return request({
|
||||
url: `${DICT_BASE_URL}/${id}`,
|
||||
method: "PUT",
|
||||
data: data,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除字典
|
||||
*
|
||||
* @param ids 字典ID,多个以英文逗号(,)分隔
|
||||
*/
|
||||
deleteByIds(ids: string) {
|
||||
return request({
|
||||
url: `${DICT_BASE_URL}/${ids}`,
|
||||
method: "delete",
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取字典列表
|
||||
*
|
||||
* @returns 字典列表
|
||||
*/
|
||||
getList() {
|
||||
return request<DictVO[]>({
|
||||
url: `${DICT_BASE_URL}/list`,
|
||||
method: "GET",
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default DictAPI;
|
||||
|
||||
/**
|
||||
* 字典查询参数
|
||||
*/
|
||||
export interface DictPageQuery extends PageQuery {
|
||||
/**
|
||||
* 关键字(字典名称/编码)
|
||||
*/
|
||||
keywords?: string;
|
||||
|
||||
/**
|
||||
* 字典状态(1:启用,0:禁用)
|
||||
*/
|
||||
status?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典分页对象
|
||||
*/
|
||||
export interface DictPageVO {
|
||||
/**
|
||||
* 字典ID
|
||||
*/
|
||||
id: number;
|
||||
/**
|
||||
* 字典名称
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* 字典编码
|
||||
*/
|
||||
dictCode: string;
|
||||
/**
|
||||
* 字典状态(1:启用,0:禁用)
|
||||
*/
|
||||
status: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典
|
||||
*/
|
||||
export interface DictForm {
|
||||
/**
|
||||
* 字典ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 字典名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 字典编码
|
||||
*/
|
||||
dictCode?: string;
|
||||
/**
|
||||
* 字典状态(1-启用,0-禁用)
|
||||
*/
|
||||
status?: number;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典数据项分页VO
|
||||
*
|
||||
* @description 字典数据分页对象
|
||||
*/
|
||||
export interface DictVO {
|
||||
/** 字典名称 */
|
||||
name: string;
|
||||
|
||||
/** 字典编码 */
|
||||
dictCode: string;
|
||||
|
||||
/** 字典数据集合 */
|
||||
dictDataList: DictData[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典数据
|
||||
*
|
||||
* @description 字典数据
|
||||
*/
|
||||
export interface DictData {
|
||||
/** 字典数据值 */
|
||||
value: string;
|
||||
|
||||
/** 字典数据标签 */
|
||||
label: string;
|
||||
|
||||
/** 标签类型 */
|
||||
tagType: string;
|
||||
}
|
||||
126
src/components/dict/index.vue
Normal file
126
src/components/dict/index.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<wd-picker
|
||||
v-if="type === 'select' || type === 'radio'"
|
||||
v-model="selectedValue"
|
||||
:columns="options"
|
||||
:label="label"
|
||||
:placeholder="placeholder"
|
||||
clearable
|
||||
:rules="rules"
|
||||
@confirm="handleChange"
|
||||
/>
|
||||
<wd-select-picker
|
||||
v-else-if="type === 'checkbox'"
|
||||
v-model="selectedValue"
|
||||
:columns="options"
|
||||
:placeholder="placeholder"
|
||||
clearable
|
||||
:label="label"
|
||||
:rules="rules"
|
||||
@confirm="handleChange"
|
||||
/>
|
||||
<wd-input
|
||||
v-else
|
||||
v-model="selectedValue"
|
||||
:label="label"
|
||||
clearable
|
||||
:placeholder="placeholder"
|
||||
:rules="rules"
|
||||
@input="handleChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useDictStore } from "@/store/modules/dict";
|
||||
|
||||
const dictStore = useDictStore();
|
||||
|
||||
const props = defineProps({
|
||||
code: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
modelValue: {
|
||||
type: [String, Number, Array],
|
||||
required: false,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: "select",
|
||||
validator: (value: string) => ["select", "radio", "checkbox"].includes(value),
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "请选择",
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rules: {
|
||||
type: Array as PropType<any[]>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const options = ref<Array<{ label: string; value: string | number }>>([]);
|
||||
|
||||
const selectedValue = ref<any>(
|
||||
typeof props.modelValue === "string" || typeof props.modelValue === "number"
|
||||
? props.modelValue
|
||||
: Array.isArray(props.modelValue)
|
||||
? props.modelValue
|
||||
: undefined
|
||||
);
|
||||
|
||||
// 监听 modelValue 变化
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
if (props.type === "checkbox") {
|
||||
selectedValue.value = Array.isArray(newValue) ? newValue : [];
|
||||
} else {
|
||||
selectedValue.value = newValue?.toString() || "";
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 监听 options 变化并重新匹配 selectedValue
|
||||
watch(
|
||||
() => options.value,
|
||||
(newOptions) => {
|
||||
// options 加载后,确保 selectedValue 可以正确匹配到 options
|
||||
if (newOptions.length > 0 && selectedValue.value !== undefined) {
|
||||
const matchedOption = newOptions.find((option) => option.value === selectedValue.value);
|
||||
if (!matchedOption && props.type !== "checkbox") {
|
||||
// 如果找不到匹配项,清空选中
|
||||
selectedValue.value = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 监听 selectedValue 的变化并触发 update:modelValue
|
||||
function handleChange(val: any) {
|
||||
emit("update:modelValue", val.value);
|
||||
}
|
||||
|
||||
// 获取字典数据
|
||||
onMounted(async () => {
|
||||
if (!props.type) {
|
||||
return;
|
||||
}
|
||||
let dictData = dictStore.getDictionary(props.code);
|
||||
options.value = dictData.map((item) => ({
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
54
src/components/dict/label.vue
Normal file
54
src/components/dict/label.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<template v-if="tagType">
|
||||
<wd-tag :type="tagType" :round="round">{{ label }}</wd-tag>
|
||||
</template>
|
||||
<template v-else>
|
||||
<view>{{ label }}</view>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useDictStore } from "@/store/modules/dict";
|
||||
const dictStore = useDictStore();
|
||||
|
||||
const props = defineProps({
|
||||
code: String,
|
||||
modelValue: [String, Number],
|
||||
round: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
type TagType = "success" | "warning" | "primary" | "danger" | "default";
|
||||
const label = ref("");
|
||||
const tagType = ref<TagType>();
|
||||
|
||||
const getLabelAndTagByValue = async (dictCode: string, value: any) => {
|
||||
// 先从本地缓存中获取字典数据
|
||||
const dictData = dictStore.getDictionary(dictCode);
|
||||
console.log("dictData", dictData);
|
||||
// 查找对应的字典项
|
||||
const dictEntry = dictData.find((item: any) => item.value == value);
|
||||
return {
|
||||
label: dictEntry ? dictEntry.label : "",
|
||||
tag: dictEntry ? dictEntry.tagType : undefined,
|
||||
};
|
||||
};
|
||||
|
||||
// 监听 props 的变化,获取并更新 label 和 tag
|
||||
const fetchLabelAndTag = async () => {
|
||||
const result = await getLabelAndTagByValue(props.code as string, props.modelValue);
|
||||
console.log("result", result);
|
||||
label.value = result.label;
|
||||
if (result.tag === "info") {
|
||||
result.tag = "default";
|
||||
}
|
||||
tagType.value = result.tag as "success" | "warning" | "primary" | "danger" | "default";
|
||||
};
|
||||
|
||||
// 首次挂载时获取字典数据
|
||||
onMounted(fetchLabelAndTag);
|
||||
|
||||
// 当 modelValue 发生变化时重新获取
|
||||
watch(() => props.modelValue, fetchLabelAndTag);
|
||||
</script>
|
||||
@@ -2,7 +2,8 @@
|
||||
"easycom": {
|
||||
"autoscan": true,
|
||||
"custom": {
|
||||
"^wd-(.*)": "wot-design-uni/components/wd-$1/wd-$1.vue"
|
||||
"^wd-(.*)": "wot-design-uni/components/wd-$1/wd-$1.vue",
|
||||
"^dict-(.*)": "@/components/dict/$1.vue"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -55,12 +56,6 @@
|
||||
"navigationBarTitleText": "系统配置"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/work/config/edit",
|
||||
"style": {
|
||||
"navigationBarTitleText": "新增修改配置页面"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/work/notice/index",
|
||||
"style": {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { type LoginFormData } from "@/api/auth";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
|
||||
import { useDictStore } from "@/store/modules/dict";
|
||||
const loginFormRef = ref();
|
||||
|
||||
const loginFormData = ref<LoginFormData>({
|
||||
@@ -44,7 +44,7 @@ const loginFormData = ref<LoginFormData>({
|
||||
});
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
const dictStore = useDictStore();
|
||||
// 登录处理
|
||||
const handleLogin = () => {
|
||||
loginFormRef.value.validate().then(async ({ valid }: { valid: boolean }) => {
|
||||
@@ -52,6 +52,7 @@ const handleLogin = () => {
|
||||
try {
|
||||
await userStore.login(loginFormData.value); // 等待登录和获取用户信息完成
|
||||
await userStore.getInfo(); // 等待用户信息获取完成
|
||||
await dictStore.loadDictionaries(); // 等待字典数据加载完成
|
||||
uni.showToast({ title: "登录成功", icon: "success" });
|
||||
const pages = getCurrentPages(); // 获取当前的页面栈
|
||||
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
<template>
|
||||
<view class="form-container">
|
||||
<wd-form ref="formRef" :model="form">
|
||||
<wd-cell-group border>
|
||||
<wd-input
|
||||
v-model="form.configName"
|
||||
label="配置名称"
|
||||
label-width="100px"
|
||||
label-align="right"
|
||||
prop="configName"
|
||||
clearable
|
||||
placeholder="请输入配置名称"
|
||||
:rules="[{ required: true, message: '请填写配置名称' }]"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="form.configKey"
|
||||
label="配置键"
|
||||
label-width="100px"
|
||||
label-align="right"
|
||||
prop="configKey"
|
||||
clearable
|
||||
placeholder="请输入配置键"
|
||||
:rules="[{ required: true, message: '请填写配置键' }]"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="form.configValue"
|
||||
label="配置值"
|
||||
label-width="100px"
|
||||
label-align="right"
|
||||
prop="configValue"
|
||||
clearable
|
||||
placeholder="请输入配置值"
|
||||
:rules="[{ required: true, message: '请填写配置值' }]"
|
||||
/>
|
||||
<wd-textarea
|
||||
v-model="form.remark"
|
||||
prop="remark"
|
||||
label="配置描述"
|
||||
label-align="right"
|
||||
clearable
|
||||
:maxlength="100"
|
||||
show-word-limit
|
||||
label-width="100px"
|
||||
placeholder="请输入配置描述"
|
||||
/>
|
||||
</wd-cell-group>
|
||||
<view class="footer">
|
||||
<view class="button-container">
|
||||
<wd-button type="info" size="large" block @click="handleBack">返回</wd-button>
|
||||
<wd-button
|
||||
type="primary"
|
||||
size="large"
|
||||
native-type="submit"
|
||||
:disabled="subDisable"
|
||||
block
|
||||
@click="handleSubmit"
|
||||
>
|
||||
提交
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-form>
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import ConfigAPI, { ConfigForm, ConfigPageVO } from "@/api/system/config";
|
||||
import { FormInstance } from "wot-design-uni/components/wd-form/types";
|
||||
|
||||
const form = ref<ConfigForm>({
|
||||
id: undefined,
|
||||
configName: "",
|
||||
configKey: "",
|
||||
configValue: "",
|
||||
remark: "",
|
||||
});
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const subDisable = ref(false);
|
||||
|
||||
/**
|
||||
* 返回
|
||||
*/
|
||||
const handleBack = () => {
|
||||
uni.navigateBack();
|
||||
};
|
||||
|
||||
/**
|
||||
* 提交
|
||||
*/
|
||||
const handleSubmit = () => {
|
||||
subDisable.value = true;
|
||||
if (formRef.value) {
|
||||
formRef.value!.validate().then(({ valid, errors }) => {
|
||||
if (valid) {
|
||||
if (form.value.id) {
|
||||
// 如果 id 不为 null,调用更新 API
|
||||
ConfigAPI.update(form.value.id as number, form.value)
|
||||
.then((response) => {
|
||||
uni.showToast({
|
||||
title: "更新成功",
|
||||
icon: "success",
|
||||
duration: 2000,
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("更新失败:", error);
|
||||
subDisable.value = false;
|
||||
});
|
||||
} else {
|
||||
// 如果 id 为 null,调用新增 API
|
||||
ConfigAPI.add(form.value)
|
||||
.then((response) => {
|
||||
uni.showToast({
|
||||
title: "提交成功",
|
||||
icon: "success",
|
||||
duration: 2000,
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("提交失败:", error);
|
||||
subDisable.value = false;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: "请按照要求填写表单",
|
||||
icon: "error",
|
||||
duration: 2000,
|
||||
});
|
||||
subDisable.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 定义接收的参数
|
||||
const item = ref<ConfigPageVO | null>(null);
|
||||
|
||||
// 获取传递的参数
|
||||
onLoad((options) => {
|
||||
const title = options && options.item ? "编辑配置" : "新增配置";
|
||||
uni.setNavigationBarTitle({
|
||||
title: title,
|
||||
});
|
||||
if (options && options.item) {
|
||||
try {
|
||||
item.value = JSON.parse(decodeURIComponent(options.item));
|
||||
if (item.value) {
|
||||
form.value = {
|
||||
id: item.value.id,
|
||||
configName: item.value.configName,
|
||||
configKey: item.value.configKey,
|
||||
configValue: item.value.configValue,
|
||||
remark: item.value.remark,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("解析参数失败:", error);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
padding: 40rpx;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 40rpx;
|
||||
background: #fff;
|
||||
border-radius: 8rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.button-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button-container .wd-button {
|
||||
flex: 1;
|
||||
margin: 0 5px; /* 调整按钮之间的间距 */
|
||||
}
|
||||
|
||||
.wd-cell-group {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.wd-button {
|
||||
width: 100%;
|
||||
margin-top: 10rpx; /* 调整按钮间距 */
|
||||
}
|
||||
</style>
|
||||
@@ -12,8 +12,10 @@
|
||||
placeholder="请输入关键字"
|
||||
/>
|
||||
<view class="flex flex-row items-center mb-20rpx">
|
||||
<wd-button class="mt-20rpx mb-20rpx" size="medium" @click="search()">查询</wd-button>
|
||||
<wd-button size="medium" type="info" @click="reset">重置</wd-button>
|
||||
<wd-button class="mt-20rpx mb-20rpx" size="medium" @click="handleQuery()">
|
||||
查询
|
||||
</wd-button>
|
||||
<wd-button size="medium" type="info" @click="handleReset">重置</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-drop-menu-item>
|
||||
@@ -57,9 +59,7 @@
|
||||
</wd-row>
|
||||
|
||||
<template #footer>
|
||||
<view class="flex justify-end pr-20rpx">
|
||||
<wd-button size="small" type="primary" @click="handleAction(item)">操作</wd-button>
|
||||
</view>
|
||||
<wd-button size="small" type="primary" @click="handleAction(item)">···</wd-button>
|
||||
</template>
|
||||
</wd-card>
|
||||
</view>
|
||||
@@ -69,10 +69,55 @@
|
||||
<wd-loadmore :state="state" @reload="handleQuery" />
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<view class="fixed bottom-0 left-0 right-0 flex justify-around p-20rpx bg-#fff">
|
||||
<wd-button size="medium" type="primary" @click="add">添加</wd-button>
|
||||
<view class="fixed bottom-0 w-full flex justify-around items-center p-20rpx bg-#fff">
|
||||
<wd-button size="medium" type="primary" @click="handleOpenDialog">添加</wd-button>
|
||||
<wd-button size="medium" type="success" @click="refreshCache">刷新缓存</wd-button>
|
||||
</view>
|
||||
|
||||
<wd-popup v-model="showEditPopup" position="bottom">
|
||||
<view class="p-20rpx">
|
||||
<wd-form ref="formRef" :model="form">
|
||||
<wd-input
|
||||
v-model="form.configName"
|
||||
label="配置名称"
|
||||
type="text"
|
||||
placeholder="请输入配置名称"
|
||||
:rules="[{ required: true, message: '请填写配置名称' }]"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="form.configKey"
|
||||
label="配置键名"
|
||||
type="text"
|
||||
placeholder="请输入配置键名"
|
||||
:rules="[{ required: true, message: '请填写配置键' }]"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="form.configValue"
|
||||
label="配置键值"
|
||||
type="text"
|
||||
placeholder="请输入配置键值"
|
||||
:rules="[{ required: true, message: '请填写配置值' }]"
|
||||
/>
|
||||
<wd-textarea
|
||||
v-model="form.remark"
|
||||
prop="remark"
|
||||
label="配置描述"
|
||||
label-align="right"
|
||||
clearable
|
||||
:maxlength="100"
|
||||
show-word-limit
|
||||
label-width="100px"
|
||||
placeholder="请输入配置描述"
|
||||
/>
|
||||
</wd-form>
|
||||
<view class="flex justify-around mt-20rpx">
|
||||
<wd-button @click="showEditPopup = false">取消</wd-button>
|
||||
<wd-button type="primary" native-type="submit" :loading="loading" @click="submitForm">
|
||||
确定
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -80,7 +125,8 @@
|
||||
import ConfigAPI, { ConfigPageVO, ConfigForm, ConfigPageQuery } from "@/api/system/config";
|
||||
import { DropMenuItemExpose } from "wot-design-uni/components/wd-drop-menu-item/types";
|
||||
import { LoadMoreState } from "wot-design-uni/components/wd-loadmore/types";
|
||||
import { debounce } from "@/utils/commonUtil";
|
||||
import { FormInstance } from "wot-design-uni/components/wd-form/types";
|
||||
import { debounce } from "@/utils";
|
||||
const state = ref<LoadMoreState>("loading"); // 加载状态 loading, finished:, error
|
||||
const total = ref(0);
|
||||
const queryParams = reactive<ConfigPageQuery>({
|
||||
@@ -90,6 +136,10 @@ const queryParams = reactive<ConfigPageQuery>({
|
||||
});
|
||||
// 系统配置表格数据
|
||||
const pageData = ref<ConfigPageVO[]>([]);
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
/**
|
||||
* 搜索栏
|
||||
*/
|
||||
@@ -98,17 +148,57 @@ const dropMenu = ref<DropMenuItemExpose>();
|
||||
/**
|
||||
* 搜索
|
||||
*/
|
||||
function search() {
|
||||
function handleQuery() {
|
||||
pageData.value = [];
|
||||
dropMenu.value?.close();
|
||||
queryParams.pageNum = 1;
|
||||
handleQuery();
|
||||
loadmore();
|
||||
}
|
||||
|
||||
const showEditPopup = ref(false);
|
||||
const form = ref<ConfigForm>({});
|
||||
|
||||
// 修改编辑函数
|
||||
function handleEdit(id: number) {
|
||||
ConfigAPI.getFormData(id).then((data) => {
|
||||
Object.assign(form.value, data);
|
||||
// 显示弹窗
|
||||
showEditPopup.value = true;
|
||||
});
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
async function submitForm() {
|
||||
loading.value = true;
|
||||
try {
|
||||
if (formRef.value) {
|
||||
formRef.value!.validate().then(async ({ valid, errors }) => {
|
||||
if (valid) {
|
||||
if (form.value.id) {
|
||||
await ConfigAPI.update(form.value.id, form.value);
|
||||
uni.showToast({ title: "更新成功", icon: "success" });
|
||||
} else {
|
||||
await ConfigAPI.add(form.value);
|
||||
uni.showToast({ title: "添加成功", icon: "success" });
|
||||
}
|
||||
showEditPopup.value = false;
|
||||
handleQuery(); // 刷新列表
|
||||
} else {
|
||||
uni.showToast({ title: "请检查表单", icon: "error" });
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
uni.showToast({ title: "操作失败", icon: "error" });
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置搜索条件
|
||||
*/
|
||||
function reset() {
|
||||
function handleReset() {
|
||||
queryParams.pageNum = 1;
|
||||
queryParams.keywords = "";
|
||||
pageData.value = [];
|
||||
@@ -125,9 +215,9 @@ onReachBottom(() => {
|
||||
});
|
||||
|
||||
/**
|
||||
* 查询
|
||||
* 加载更多
|
||||
*/
|
||||
function handleQuery() {
|
||||
function loadmore() {
|
||||
state.value = "loading";
|
||||
ConfigAPI.getPage(queryParams)
|
||||
.then((data) => {
|
||||
@@ -146,10 +236,13 @@ function handleQuery() {
|
||||
/**
|
||||
* 添加
|
||||
*/
|
||||
function add() {
|
||||
uni.navigateTo({
|
||||
url: "/pages/work/config/edit",
|
||||
});
|
||||
function handleOpenDialog() {
|
||||
form.id = undefined;
|
||||
form.configName = "";
|
||||
form.configKey = "";
|
||||
form.configValue = "";
|
||||
form.remark = "";
|
||||
showEditPopup.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,16 +258,6 @@ const refreshCache = debounce(() => {
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*/
|
||||
function handleEdit(item: ConfigPageVO) {
|
||||
// 直接传递整个 item 对象
|
||||
uni.navigateTo({
|
||||
url: "/pages/work/config/edit?item=" + encodeURIComponent(JSON.stringify(item)),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
@@ -204,7 +287,7 @@ function handleAction(item: ConfigPageVO) {
|
||||
success: ({ tapIndex }) => {
|
||||
switch (actions[tapIndex]) {
|
||||
case "编辑":
|
||||
handleEdit(item);
|
||||
handleEdit(item.id || 0);
|
||||
break;
|
||||
case "删除":
|
||||
handleDelete(item);
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
allow-same-day
|
||||
@confirm="handleConfirm"
|
||||
/>
|
||||
<view class="flex flex-row items-center mb-20rpx">
|
||||
<wd-button class="mt-20rpx mb-20rpx" size="medium" @click="search()">查询</wd-button>
|
||||
<wd-button size="medium" type="info" @click="reset">重置</wd-button>
|
||||
<view class="flex-between mb-20rpx">
|
||||
<wd-button class="mt-20rpx mb-20rpx" size="medium" @click="handleSearch">
|
||||
查询
|
||||
</wd-button>
|
||||
<wd-button size="medium" type="info" @click="handleReset">重置</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-drop-menu-item>
|
||||
@@ -90,7 +92,7 @@ function handleConfirm({ value }: Ref<number[]>) {
|
||||
* 搜索栏
|
||||
*/
|
||||
const dropMenu = ref<DropMenuItemExpose>();
|
||||
function search() {
|
||||
function handleSearch() {
|
||||
pageData.value = [];
|
||||
dropMenu.value?.close();
|
||||
handleQuery();
|
||||
@@ -99,7 +101,7 @@ function search() {
|
||||
/**
|
||||
* 重置搜索条件
|
||||
*/
|
||||
function reset() {
|
||||
function handleReset() {
|
||||
queryParams.pageNum = 1;
|
||||
queryParams.keywords = "";
|
||||
queryParams.createTime = ["", ""];
|
||||
|
||||
@@ -8,18 +8,11 @@
|
||||
</h2>
|
||||
<div class="notice-meta">
|
||||
<div class="meta-row">
|
||||
<span>发布人:{{ noticeDetail.publisherName }}</span>
|
||||
<wd-divider direction="vertical" />
|
||||
<span>发布时间:{{ formatDate(noticeDetail.publishTime ?? "") }}</span>
|
||||
</div>
|
||||
<div class="meta-row">
|
||||
<span class="priority-wrapper">
|
||||
优先级:
|
||||
<wd-tag :type="getLevelType(noticeDetail.level) as TagType">
|
||||
{{ getLevelText(noticeDetail.level) }}
|
||||
</wd-tag>
|
||||
</span>
|
||||
优先级:
|
||||
<dict-label code="notice_level" :model-value="noticeDetail.level" />
|
||||
</div>
|
||||
<div class="meta-row">发布人:{{ noticeDetail.publisherName }}</div>
|
||||
<div class="meta-row">发布时间:{{ noticeDetail.publishTime }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<wd-divider />
|
||||
@@ -29,10 +22,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import type { NoticeDetailVO } from "@/api/system/notice";
|
||||
import NoticeAPI from "@/api/system/notice";
|
||||
import { TagType } from "wot-design-uni/components/wd-tag/types";
|
||||
|
||||
const noticeDetail = ref<NoticeDetailVO>({});
|
||||
|
||||
@@ -44,8 +35,8 @@ const getLevelText = (level?: string) => {
|
||||
};
|
||||
return textMap[level || "L"];
|
||||
};
|
||||
const getLevelType = (level?: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
const getLevelType = (level?: string): "primary" | "warning" | "danger" => {
|
||||
const typeMap: Record<string, "primary" | "warning" | "danger"> = {
|
||||
L: "primary",
|
||||
M: "warning",
|
||||
H: "danger",
|
||||
@@ -67,15 +58,6 @@ const getNoticeDetail = async (id: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 格式化日期
|
||||
* @param date 日期
|
||||
* @returns 格式化后的日期
|
||||
*/
|
||||
const formatDate = (date: string | Date): string => {
|
||||
return date ? date.toString().split(" ")[0] : "-";
|
||||
};
|
||||
|
||||
onLoad((options: any) => {
|
||||
if (options && options.id) {
|
||||
getNoticeDetail(options.id as string);
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
/>
|
||||
|
||||
<view class="flex flex-row items-center mb-20rpx">
|
||||
<wd-button class="mt-20rpx mb-20rpx" size="medium" @click="handleSearch()">
|
||||
<wd-button class="mt-20rpx mb-20rpx" size="medium" @click="handleQuery()">
|
||||
查询
|
||||
</wd-button>
|
||||
<wd-button size="medium" type="info" @click="reset">重置</wd-button>
|
||||
<wd-button size="medium" type="info" @click="handleReset">重置</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-drop-menu-item>
|
||||
@@ -33,33 +33,15 @@
|
||||
</view>
|
||||
</template>
|
||||
<wd-row class="mb-20rpx">
|
||||
<wd-col :span="12">
|
||||
<wd-col :span="12">
|
||||
<wd-col :span="24">
|
||||
<wd-col :span="8">
|
||||
<view>通告目标类型:</view>
|
||||
</wd-col>
|
||||
<wd-col :span="12">
|
||||
<wd-col :span="16">
|
||||
<view>{{ item.targetType === 1 ? "指定" : "全体" }}</view>
|
||||
</wd-col>
|
||||
</wd-col>
|
||||
<wd-col v-if="item.publishStatus === 1" :span="12">
|
||||
<wd-col :span="8">
|
||||
<view>发布时间:</view>
|
||||
</wd-col>
|
||||
<wd-col :span="16">
|
||||
<view>{{ formatDate(item.publishTime) }}</view>
|
||||
</wd-col>
|
||||
</wd-col>
|
||||
<wd-col v-else :span="12">
|
||||
<wd-col :span="8">
|
||||
<view>撤回时间:</view>
|
||||
</wd-col>
|
||||
<wd-col :span="16">
|
||||
<view>{{ formatDate(item.revokeTime) }}</view>
|
||||
</wd-col>
|
||||
</wd-col>
|
||||
</wd-row>
|
||||
<wd-row class="mb-20rpx">
|
||||
<wd-col :span="12">
|
||||
<wd-col :span="24">
|
||||
<wd-col :span="8">
|
||||
<view>发布人:</view>
|
||||
</wd-col>
|
||||
@@ -67,21 +49,34 @@
|
||||
<view>{{ item.publisherName || "-" }}</view>
|
||||
</wd-col>
|
||||
</wd-col>
|
||||
<wd-col :span="12">
|
||||
<wd-col v-if="item.publishStatus === 1" :span="24">
|
||||
<wd-col :span="8">
|
||||
<view>发布时间:</view>
|
||||
</wd-col>
|
||||
<wd-col :span="16">
|
||||
<view>{{ formatDate(item.publishTime) }}</view>
|
||||
</wd-col>
|
||||
</wd-col>
|
||||
<wd-col v-else :span="24">
|
||||
<wd-col :span="8">
|
||||
<view>撤回时间:</view>
|
||||
</wd-col>
|
||||
<wd-col :span="16">
|
||||
<view>{{ formatDate(item.revokeTime) }}</view>
|
||||
</wd-col>
|
||||
</wd-col>
|
||||
<wd-col :span="24">
|
||||
<wd-col :span="8">
|
||||
<view>紧急程度:</view>
|
||||
</wd-col>
|
||||
<wd-col :span="16">
|
||||
<view>{{ getLevelType(item.level) || "-" }}</view>
|
||||
<dict-label code="notice_level" :model-value="item.level" />
|
||||
</wd-col>
|
||||
</wd-col>
|
||||
</wd-row>
|
||||
|
||||
<template #footer>
|
||||
<view class="flex justify-end gap-20rpx">
|
||||
<wd-button size="small" plain @click="handleView(item)">查看详情</wd-button>
|
||||
<wd-button size="small" type="primary" @click="handleAction(item)">更多操作</wd-button>
|
||||
</view>
|
||||
<wd-button size="small" type="primary" @click="handleAction(item)">···</wd-button>
|
||||
</template>
|
||||
</wd-card>
|
||||
</view>
|
||||
@@ -92,20 +87,12 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { LoadMoreState } from "wot-design-uni/components/wd-loadmore/types";
|
||||
import { DropMenuItemExpose } from "wot-design-uni/components/wd-drop-menu-item/types";
|
||||
import NoticeAPI, { NoticePageQuery, NoticePageVO } from "@/api/system/notice";
|
||||
const loadState = ref<LoadMoreState>("finished");
|
||||
const dataList = ref<NoticePageVO[]>([]);
|
||||
const total = ref(0);
|
||||
// 添加新的响应式数据
|
||||
const statusOptions = [
|
||||
{ value: "", label: "全部状态" },
|
||||
{ value: 0, label: "未发布" },
|
||||
{ value: 1, label: "已发布" },
|
||||
{ value: -1, label: "已撤回" },
|
||||
];
|
||||
|
||||
// 修改查询参数
|
||||
const queryParams = ref<NoticePageQuery>({
|
||||
@@ -116,14 +103,14 @@ const queryParams = ref<NoticePageQuery>({
|
||||
|
||||
// 添加搜索处理函数
|
||||
const dropMenu = ref<DropMenuItemExpose>();
|
||||
const handleSearch = () => {
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
loadMore();
|
||||
dropMenu.value?.close();
|
||||
};
|
||||
|
||||
// 重置
|
||||
const reset = () => {
|
||||
const handleReset = () => {
|
||||
queryParams.value = { pageNum: 1, pageSize: 10 };
|
||||
dropMenu.value?.close();
|
||||
loadMore();
|
||||
@@ -201,13 +188,13 @@ const handleView = (notice: NoticePageVO) => {
|
||||
|
||||
// 操作按钮
|
||||
const handleAction = (notice: NoticePageVO) => {
|
||||
const actions = notice.publishStatus !== 1 ? ["删除", "发布"] : ["撤回"];
|
||||
const actions = notice.publishStatus !== 1 ? ["查看", "删除", "发布"] : ["查看", "撤回"];
|
||||
uni.showActionSheet({
|
||||
itemList: actions,
|
||||
success: ({ tapIndex }) => {
|
||||
switch (actions[tapIndex]) {
|
||||
case "编辑":
|
||||
handleEdit(notice);
|
||||
case "查看":
|
||||
handleView(notice);
|
||||
break;
|
||||
case "删除":
|
||||
handleDelete(notice);
|
||||
@@ -216,42 +203,13 @@ const handleAction = (notice: NoticePageVO) => {
|
||||
handlePublish(notice);
|
||||
break;
|
||||
case "撤回":
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
content: "确定要撤回该通知吗?",
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
await NoticeAPI.revoke(Number(notice.id));
|
||||
uni.showToast({
|
||||
title: "撤回成功",
|
||||
icon: "success",
|
||||
});
|
||||
// 刷新列表
|
||||
queryParams.value.pageNum = 1;
|
||||
loadMore();
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: "撤回失败",
|
||||
icon: "error",
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
handleRevoke(notice);
|
||||
break;
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (notice: NoticePageVO) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/work/notice/edit?id=${notice.id}`,
|
||||
});
|
||||
};
|
||||
|
||||
// 删除
|
||||
const handleDelete = (notice: NoticePageVO) => {
|
||||
uni.showModal({
|
||||
@@ -294,6 +252,33 @@ const handlePublish = (notice: NoticePageVO) => {
|
||||
});
|
||||
};
|
||||
|
||||
// 撤回
|
||||
const handleRevoke = (notice: NoticePageVO) => {
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
content: "确定要撤回该通知吗?",
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
await NoticeAPI.revoke(Number(notice.id));
|
||||
uni.showToast({
|
||||
title: "撤回成功",
|
||||
icon: "success",
|
||||
});
|
||||
// 刷新列表
|
||||
queryParams.value.pageNum = 1;
|
||||
loadMore();
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: "撤回失败",
|
||||
icon: "error",
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
onReachBottom(() => {
|
||||
if (loadState.value === "loading" || loadState.value === "finished") return;
|
||||
loadMore();
|
||||
|
||||
38
src/store/modules/dict.ts
Normal file
38
src/store/modules/dict.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { defineStore } from "pinia";
|
||||
import DictAPI, { type DictVO, type DictData } from "@/api/system/dict";
|
||||
import { setDictCache, getDictCache } from "@/utils/cache";
|
||||
export const useDictStore = defineStore("dict", () => {
|
||||
const dictionary = ref<Record<string, DictData[]>>(getDictCache());
|
||||
|
||||
const setDictionary = (dict: DictVO) => {
|
||||
dictionary.value[dict.dictCode] = dict.dictDataList;
|
||||
setDictCache(dictionary.value);
|
||||
};
|
||||
|
||||
const loadDictionaries = async () => {
|
||||
const dictList = await DictAPI.getList();
|
||||
dictList.forEach(setDictionary);
|
||||
};
|
||||
|
||||
const getDictionary = (dictCode: string): DictData[] => {
|
||||
return dictionary.value[dictCode] || [];
|
||||
};
|
||||
|
||||
const clearDictionaryCache = () => {
|
||||
dictionary.value = {};
|
||||
};
|
||||
|
||||
const updateDictionaryCache = async () => {
|
||||
clearDictionaryCache(); // 先清除旧缓存
|
||||
await loadDictionaries(); // 重新加载最新字典数据
|
||||
};
|
||||
|
||||
return {
|
||||
dictionary,
|
||||
setDictionary,
|
||||
loadDictionaries,
|
||||
getDictionary,
|
||||
clearDictionaryCache,
|
||||
updateDictionaryCache,
|
||||
};
|
||||
});
|
||||
@@ -1,5 +1,7 @@
|
||||
const TOKEN_KEY = "app-token";
|
||||
const USER_INFO_KEY = "user-info";
|
||||
const DICT_KEY = "dict";
|
||||
import { type DictData } from "@/api/system/dict";
|
||||
|
||||
// 设置 token
|
||||
export function setToken(token: string) {
|
||||
@@ -31,8 +33,24 @@ export function clearUserInfo() {
|
||||
uni.removeStorageSync(USER_INFO_KEY);
|
||||
}
|
||||
|
||||
// 设置字典缓存
|
||||
export function setDictCache(dict: Record<string, DictData[]>) {
|
||||
uni.setStorageSync(DICT_KEY, dict);
|
||||
}
|
||||
|
||||
// 获取字典缓存
|
||||
export function getDictCache(): Record<string, DictData[]> {
|
||||
return uni.getStorageSync(DICT_KEY) || {};
|
||||
}
|
||||
|
||||
// 清除字典缓存
|
||||
export function clearDictCache() {
|
||||
uni.removeStorageSync(DICT_KEY);
|
||||
}
|
||||
|
||||
// 清除所有缓存信息
|
||||
export function clearAll() {
|
||||
clearToken();
|
||||
clearUserInfo();
|
||||
clearDictCache();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export default function request<T>(options: UniApp.RequestOptions): Promise<T> {
|
||||
...options.header,
|
||||
Authorization: getToken() ? `Bearer ${getToken()}` : "",
|
||||
},
|
||||
data: handleData(options.data, options.method || "GET"),
|
||||
success: (response) => {
|
||||
console.log("success response", response);
|
||||
const resData = response.data as ResponseData<T>;
|
||||
@@ -59,3 +60,51 @@ export default function request<T>(options: UniApp.RequestOptions): Promise<T> {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理请求数据
|
||||
* @param data 请求数据
|
||||
* @returns 处理后的数据
|
||||
*/
|
||||
function handleData(data: any, method: string) {
|
||||
// 非微信小程序,且非GET请求,则不处理数据
|
||||
const appInfo = uni.getAppBaseInfo();
|
||||
if (method !== "GET" && appInfo.hostName !== "WeChat") {
|
||||
return data;
|
||||
}
|
||||
if (!data) return data;
|
||||
|
||||
// 如果是对象,遍历处理每个属性
|
||||
if (typeof data === "object") {
|
||||
const result: Record<string, any> = {};
|
||||
for (const key in data) {
|
||||
const value = data[key];
|
||||
if (Array.isArray(value)) {
|
||||
let res = handleArray(value);
|
||||
if (res) {
|
||||
result[key] = res;
|
||||
}
|
||||
} else {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理数组
|
||||
* @param value 数组
|
||||
* @returns 逗号分隔字符串
|
||||
*/
|
||||
function handleArray(value: any[]) {
|
||||
let str = "";
|
||||
for (const item of value) {
|
||||
if (item != 0 && item) {
|
||||
str += `${item},`;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user