feat(字典管理): 新增字典管理功能及相关基础设施
- 新增字典管理页面,支持字典项的增删改查 - 添加 pinia-plugin-persistedstate 依赖,实现状态持久化 - 创建字典相关的 API 类型定义和服务接口 - 实现字典数据存储模块,支持字典数据的缓存和复用 - 添加 useDict 组合式函数,方便组件中使用字典数据 - 在题目分类和比赛创建模块中使用字典数据替代硬编码选项 - 优化分类树组件,添加拖拽手柄和题目类型选择
This commit is contained in:
@ -4,6 +4,7 @@ import { darkTheme, NConfigProvider } from 'naive-ui'
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { naiveDateLocales, naiveLocales } from './locales/naive'
|
import { naiveDateLocales, naiveLocales } from './locales/naive'
|
||||||
import { useAppStore } from './store/modules/app'
|
import { useAppStore } from './store/modules/app'
|
||||||
|
import { useBusinessStore } from './store/modules/business'
|
||||||
import { useThemeStore } from './store/modules/theme'
|
import { useThemeStore } from './store/modules/theme'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@ -12,6 +13,9 @@ defineOptions({
|
|||||||
|
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
const themeStore = useThemeStore()
|
const themeStore = useThemeStore()
|
||||||
|
const businessStore = useBusinessStore()
|
||||||
|
/* 初始化字典 */
|
||||||
|
businessStore.initDict()
|
||||||
|
|
||||||
const naiveDarkTheme = computed(() => (themeStore.darkMode ? darkTheme : undefined))
|
const naiveDarkTheme = computed(() => (themeStore.darkMode ? darkTheme : undefined))
|
||||||
|
|
||||||
|
|||||||
29
apps/admin/src/hooks/business/useDict.ts
Normal file
29
apps/admin/src/hooks/business/useDict.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { computed, onMounted } from 'vue'
|
||||||
|
import { useBusinessStore } from '@/store/modules/business'
|
||||||
|
|
||||||
|
export function useDict(key: string) {
|
||||||
|
const store = useBusinessStore()
|
||||||
|
|
||||||
|
const data = computed(() => store.dictData[key] || [])
|
||||||
|
const loading = computed(() => store.loadingMap[key] || false)
|
||||||
|
|
||||||
|
const options = computed(() => {
|
||||||
|
if (data.value && data.value.length > 0) {
|
||||||
|
return data.value.map((item: any) => ({
|
||||||
|
label: item.uI_Key || item.UI_Key || item.DicKey,
|
||||||
|
value: item.uI_Value || item.UI_Value || item.DicValue,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
store.getDict(key)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
options,
|
||||||
|
loading,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -242,6 +242,7 @@ const local: App.I18n.Schema = {
|
|||||||
'competition_competition-add': 'Competition Add',
|
'competition_competition-add': 'Competition Add',
|
||||||
'competition_competition-detail': 'Competition Detail',
|
'competition_competition-detail': 'Competition Detail',
|
||||||
'competition_competition-list': 'Competition List',
|
'competition_competition-list': 'Competition List',
|
||||||
|
'dictionary': 'Dictionary',
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
login: {
|
login: {
|
||||||
|
|||||||
@ -238,6 +238,7 @@ const local: App.I18n.Schema = {
|
|||||||
'competition_competition-add': '比赛添加',
|
'competition_competition-add': '比赛添加',
|
||||||
'competition_competition-detail': '比赛详情',
|
'competition_competition-detail': '比赛详情',
|
||||||
'competition_competition-list': '比赛列表',
|
'competition_competition-list': '比赛列表',
|
||||||
|
'dictionary': '字典管理',
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
login: {
|
login: {
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
|
|||||||
"competition_competition-add": () => import("@/views/competition/competition-add/index.vue"),
|
"competition_competition-add": () => import("@/views/competition/competition-add/index.vue"),
|
||||||
"competition_competition-detail": () => import("@/views/competition/competition-detail/index.vue"),
|
"competition_competition-detail": () => import("@/views/competition/competition-detail/index.vue"),
|
||||||
"competition_competition-list": () => import("@/views/competition/competition-list/index.vue"),
|
"competition_competition-list": () => import("@/views/competition/competition-list/index.vue"),
|
||||||
|
dictionary: () => import("@/views/dictionary/index.vue"),
|
||||||
home: () => import("@/views/home/index.vue"),
|
home: () => import("@/views/home/index.vue"),
|
||||||
"question-store": () => import("@/views/question-store/index.vue"),
|
"question-store": () => import("@/views/question-store/index.vue"),
|
||||||
"rank_rank-detail": () => import("@/views/rank/rank-detail/index.vue"),
|
"rank_rank-detail": () => import("@/views/rank/rank-detail/index.vue"),
|
||||||
|
|||||||
@ -83,6 +83,17 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'dictionary',
|
||||||
|
path: '/dictionary',
|
||||||
|
component: 'layout.base$view.dictionary',
|
||||||
|
meta: {
|
||||||
|
title: 'dictionary',
|
||||||
|
i18nKey: 'route.dictionary',
|
||||||
|
icon: 'material-symbols:settings',
|
||||||
|
order: 6
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'home',
|
name: 'home',
|
||||||
path: '/home',
|
path: '/home',
|
||||||
|
|||||||
@ -170,6 +170,7 @@ const routeMap: RouteMap = {
|
|||||||
"competition_competition-add": "/competition/competition-add",
|
"competition_competition-add": "/competition/competition-add",
|
||||||
"competition_competition-detail": "/competition/competition-detail",
|
"competition_competition-detail": "/competition/competition-detail",
|
||||||
"competition_competition-list": "/competition/competition-list",
|
"competition_competition-list": "/competition/competition-list",
|
||||||
|
"dictionary": "/dictionary",
|
||||||
"home": "/home",
|
"home": "/home",
|
||||||
"iframe-page": "/iframe-page/:url",
|
"iframe-page": "/iframe-page/:url",
|
||||||
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
|
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
|
||||||
|
|||||||
59
apps/admin/src/service/api/dictionary.ts
Normal file
59
apps/admin/src/service/api/dictionary.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { request } from '../request'
|
||||||
|
|
||||||
|
/** Get dictionary list */
|
||||||
|
export function getDictionaryList(roomID?: number) {
|
||||||
|
return request<Api.Common.CommonResponse>({
|
||||||
|
url: '/Base/ActivityMain/GetDictionaryList',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
RoomId: roomID,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增字段项目 */
|
||||||
|
export function addOrUpdateDictionary(data: Api.Dictionary.AddOrUpdateParams) {
|
||||||
|
return request({
|
||||||
|
url: '/Base/ActivityMain/AddOrUpdateDictionary',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Delete dictionary */
|
||||||
|
export function deleteDictionary(DicID: number) {
|
||||||
|
return request({
|
||||||
|
url: `/Base/ActivityMain/DeleteDictionary/?DicID=${DicID}`,
|
||||||
|
method: 'post',
|
||||||
|
data: { DicID },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 根据字典ID查询字典类型 */
|
||||||
|
export function getDictionaryListUITypeByDicID(dicID: number) {
|
||||||
|
return request<Api.Common.CommonResponse>({
|
||||||
|
url: '/Base/ActivityMain/GetDictionaryListUITypeByDicID',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
DicID: dicID,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 根据字典ID新建下面的字典类型 */
|
||||||
|
export function addOrUpdateDictionaryUIType(data: Api.Dictionary.AddOrUpdateDictionaryUITypeParams) {
|
||||||
|
return request({
|
||||||
|
url: '/Base/ActivityMain/AddOrUpdateDictionaryUIType',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除字典项 */
|
||||||
|
export function deleteDictionaryItem(DicUIID: number) {
|
||||||
|
return request({
|
||||||
|
url: `/Base/ActivityMain/DeleteDictionaryUIType/?DicUIID=${DicUIID}`,
|
||||||
|
method: 'post',
|
||||||
|
data: { DicUIID },
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import type { App } from 'vue'
|
import type { App } from 'vue'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
|
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||||
import { resetSetupStore } from './plugins'
|
import { resetSetupStore } from './plugins'
|
||||||
|
|
||||||
/** Setup Vue store plugin pinia */
|
/** Setup Vue store plugin pinia */
|
||||||
@ -7,6 +8,7 @@ export function setupStore(app: App) {
|
|||||||
const store = createPinia()
|
const store = createPinia()
|
||||||
|
|
||||||
store.use(resetSetupStore)
|
store.use(resetSetupStore)
|
||||||
|
store.use(piniaPluginPersistedstate)
|
||||||
|
|
||||||
app.use(store)
|
app.use(store)
|
||||||
}
|
}
|
||||||
|
|||||||
70
apps/admin/src/store/modules/business/index.ts
Normal file
70
apps/admin/src/store/modules/business/index.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { getDictionaryList, getDictionaryListUITypeByDicID } from '@/service/api/dictionary'
|
||||||
|
|
||||||
|
export const useBusinessStore = defineStore('business-store', () => {
|
||||||
|
const dictList = ref<Api.Dictionary.DictionaryItem[]>([])
|
||||||
|
const dictData = ref<Record<string, Api.Dictionary.DictionaryUIItem[]>>({})
|
||||||
|
const isInitialized = ref(false)
|
||||||
|
const loadingMap = ref<Record<string, boolean>>({})
|
||||||
|
|
||||||
|
/** 初始化字典 */
|
||||||
|
async function initDict() {
|
||||||
|
if (isInitialized.value)
|
||||||
|
return
|
||||||
|
const { data: res, error } = await getDictionaryList()
|
||||||
|
if (!error && res) {
|
||||||
|
dictList.value = res.data || []
|
||||||
|
isInitialized.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取字典数据 */
|
||||||
|
async function getDict(key: string) {
|
||||||
|
if (!isInitialized.value) {
|
||||||
|
await initDict()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 已经加载过,直接返回
|
||||||
|
if (dictData.value[key] && dictData.value[key].length > 0)
|
||||||
|
return dictData.value[key]
|
||||||
|
|
||||||
|
// 查找 ID
|
||||||
|
const dict = dictList.value.find(d => d.DicValue === key)
|
||||||
|
if (!dict) {
|
||||||
|
console.warn(`Dictionary key ${key} not found`)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadingMap.value[key]) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingMap.value[key] = true
|
||||||
|
try {
|
||||||
|
const { data: res, error } = await getDictionaryListUITypeByDicID(dict.Id)
|
||||||
|
if (!error && res) {
|
||||||
|
const list = res.data || []
|
||||||
|
// 使用新对象赋值,确保触发响应式更新
|
||||||
|
dictData.value = {
|
||||||
|
...dictData.value,
|
||||||
|
[key]: list,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
loadingMap.value[key] = false
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(`获取字典数据 ${key}`, dictData.value[key])
|
||||||
|
return dictData.value[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
dictList,
|
||||||
|
dictData,
|
||||||
|
loadingMap,
|
||||||
|
initDict,
|
||||||
|
getDict,
|
||||||
|
}
|
||||||
|
}, { persist: true })
|
||||||
2
apps/admin/src/typings/api/common.d.ts
vendored
2
apps/admin/src/typings/api/common.d.ts
vendored
@ -24,7 +24,7 @@ declare namespace Api {
|
|||||||
type CommonSearchParams = Pick<Common.PaginatingCommonParams, 'currentPage' | 'pageSize'>
|
type CommonSearchParams = Pick<Common.PaginatingCommonParams, 'currentPage' | 'pageSize'>
|
||||||
|
|
||||||
/** common response */
|
/** common response */
|
||||||
interface CommonResponse {
|
export interface CommonResponse {
|
||||||
success: boolean
|
success: boolean
|
||||||
code: number
|
code: number
|
||||||
msg: string
|
msg: string
|
||||||
|
|||||||
37
apps/admin/src/typings/api/dictionary.d.ts
vendored
Normal file
37
apps/admin/src/typings/api/dictionary.d.ts
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Namespace Api
|
||||||
|
*
|
||||||
|
* All backend api type
|
||||||
|
*/
|
||||||
|
declare namespace Api {
|
||||||
|
namespace Dictionary {
|
||||||
|
interface DictionaryItem {
|
||||||
|
Id: number
|
||||||
|
DicKey: string
|
||||||
|
DicValue: string
|
||||||
|
bakValue: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DictionaryUIItem {
|
||||||
|
id: number
|
||||||
|
uI_Key: string
|
||||||
|
uI_Value: string
|
||||||
|
bakValue: string
|
||||||
|
dictionaryID: number
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddOrUpdateParams = DictionaryItem
|
||||||
|
|
||||||
|
interface AddOrUpdateDictionaryUITypeParams {
|
||||||
|
id?: number
|
||||||
|
uI_Key: string
|
||||||
|
uI_Value: string
|
||||||
|
bakValue: string
|
||||||
|
dictionaryID: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SearchParams {
|
||||||
|
RoomID: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
apps/admin/src/typings/elegant-router.d.ts
vendored
3
apps/admin/src/typings/elegant-router.d.ts
vendored
@ -24,6 +24,7 @@ declare module "@elegant-router/types" {
|
|||||||
"competition_competition-add": "/competition/competition-add";
|
"competition_competition-add": "/competition/competition-add";
|
||||||
"competition_competition-detail": "/competition/competition-detail";
|
"competition_competition-detail": "/competition/competition-detail";
|
||||||
"competition_competition-list": "/competition/competition-list";
|
"competition_competition-list": "/competition/competition-list";
|
||||||
|
"dictionary": "/dictionary";
|
||||||
"home": "/home";
|
"home": "/home";
|
||||||
"iframe-page": "/iframe-page/:url";
|
"iframe-page": "/iframe-page/:url";
|
||||||
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
|
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
|
||||||
@ -71,6 +72,7 @@ declare module "@elegant-router/types" {
|
|||||||
| "404"
|
| "404"
|
||||||
| "500"
|
| "500"
|
||||||
| "competition"
|
| "competition"
|
||||||
|
| "dictionary"
|
||||||
| "home"
|
| "home"
|
||||||
| "iframe-page"
|
| "iframe-page"
|
||||||
| "login"
|
| "login"
|
||||||
@ -103,6 +105,7 @@ declare module "@elegant-router/types" {
|
|||||||
| "competition_competition-add"
|
| "competition_competition-add"
|
||||||
| "competition_competition-detail"
|
| "competition_competition-detail"
|
||||||
| "competition_competition-list"
|
| "competition_competition-list"
|
||||||
|
| "dictionary"
|
||||||
| "home"
|
| "home"
|
||||||
| "question-store"
|
| "question-store"
|
||||||
| "rank_rank-detail"
|
| "rank_rank-detail"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { NButton, NModal, NStep, NSteps } from 'naive-ui'
|
import { NButton, NModal, NStep, NSteps } from 'naive-ui'
|
||||||
import { computed, onMounted, ref, useTemplateRef, watch } from 'vue'
|
import { computed, onMounted, ref, useTemplateRef, watch } from 'vue'
|
||||||
import { roundTypeOptions, scoreTypeOptions } from '@/constants/business'
|
import { useDict } from '@/hooks/business/useDict'
|
||||||
import { fetchCreateActivity, fetchCreateQuestion, fetchCreateTeamList, fetchGetRoomList } from '@/service/api/competition'
|
import { fetchCreateActivity, fetchCreateQuestion, fetchCreateTeamList, fetchGetRoomList } from '@/service/api/competition'
|
||||||
/** 房间相关接口 */
|
/** 房间相关接口 */
|
||||||
import BasicInfo from './modules/BasicInfo.vue'
|
import BasicInfo from './modules/BasicInfo.vue'
|
||||||
@ -19,6 +19,9 @@ const emit = defineEmits<{
|
|||||||
(e: 'success'): void
|
(e: 'success'): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const { options: roundTypeOptions } = useDict('round_type')
|
||||||
|
const { options: scoreTypeOptions } = useDict('score_type')
|
||||||
|
|
||||||
const currentStep = ref<number>(1)
|
const currentStep = ref<number>(1)
|
||||||
/** 房间列表 */
|
/** 房间列表 */
|
||||||
const roomListOptions = ref<{ label: string, value: number }[]>([])
|
const roomListOptions = ref<{ label: string, value: number }[]>([])
|
||||||
@ -42,13 +45,13 @@ const competitionData = ref({
|
|||||||
{
|
{
|
||||||
id: 'round_default',
|
id: 'round_default',
|
||||||
name: '星·辞海遨游',
|
name: '星·辞海遨游',
|
||||||
roundType: roundTypeOptions[0].value,
|
roundType: roundTypeOptions.value?.[0]?.value,
|
||||||
questions: Array.from({ length: 10 }).map(() => ({
|
questions: Array.from({ length: 10 }).map(() => ({
|
||||||
value: null,
|
value: null,
|
||||||
score: 5,
|
score: 5,
|
||||||
time: 30,
|
time: 30,
|
||||||
title: '第1题',
|
title: '第1题',
|
||||||
scoreType: scoreTypeOptions[0].value,
|
scoreType: scoreTypeOptions.value?.[0]?.value,
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
import { NButton, NForm, NFormItem, NInput, NInputNumber, NModal, NSelect, type SelectOption } from 'naive-ui'
|
import { NButton, NForm, NFormItem, NInput, NInputNumber, NModal, NSelect, type SelectOption } from 'naive-ui'
|
||||||
import { onMounted, ref, watch } from 'vue'
|
import { onMounted, ref, watch } from 'vue'
|
||||||
import { VueDraggable } from 'vue-draggable-plus'
|
import { VueDraggable } from 'vue-draggable-plus'
|
||||||
import { roundTypeOptions, scoreTypeOptions, timeOptions, uiTypeOptions } from '@/constants/business'
|
import { useDict } from '@/hooks/business/useDict'
|
||||||
import { fetchGetQuestionListAll } from '@/service/api/question'
|
import { fetchGetQuestionListAll } from '@/service/api/question'
|
||||||
import { fetchTemplateList } from '@/service/api/template'
|
import { fetchTemplateList } from '@/service/api/template'
|
||||||
import { useQuestionConfig } from './useQuestionConfig'
|
import { useQuestionConfig } from './useQuestionConfig'
|
||||||
@ -26,6 +26,12 @@ const props = defineProps<{
|
|||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const { options: roundTypeOptions } = useDict('round_type')
|
||||||
|
const { options: scoreTypeOptions } = useDict('score_type')
|
||||||
|
const { options: timeOptions } = useDict('time_out')
|
||||||
|
const { options: uiTypeOptions } = useDict('ui_type')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
// getRoundScore,
|
// getRoundScore,
|
||||||
getRoundTime,
|
getRoundTime,
|
||||||
@ -47,14 +53,14 @@ const showAddModal = ref(false)
|
|||||||
const newRoundForm = ref({
|
const newRoundForm = ref({
|
||||||
name: '',
|
name: '',
|
||||||
count: 10,
|
count: 10,
|
||||||
roundType: roundTypeOptions[0].value,
|
roundType: roundTypeOptions?.value?.[0].value || '',
|
||||||
})
|
})
|
||||||
|
|
||||||
function handleOpenAddModal() {
|
function handleOpenAddModal() {
|
||||||
newRoundForm.value = {
|
newRoundForm.value = {
|
||||||
name: `第${(props.modelValue?.rounds?.length || 0) + 1}轮 星·诗词大会`,
|
name: `第${(props.modelValue?.rounds?.length || 0) + 1}轮 星·诗词大会`,
|
||||||
count: 10,
|
count: 10,
|
||||||
roundType: roundTypeOptions[0].value,
|
roundType: roundTypeOptions?.value?.[0].value || '',
|
||||||
}
|
}
|
||||||
showAddModal.value = true
|
showAddModal.value = true
|
||||||
}
|
}
|
||||||
|
|||||||
218
apps/admin/src/views/dictionary/index.vue
Normal file
218
apps/admin/src/views/dictionary/index.vue
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { DataTableColumns } from 'naive-ui'
|
||||||
|
import { NButton, NDataTable, NForm, NFormItem, NInput, NModal, NPopconfirm, NSpace, useMessage } from 'naive-ui'
|
||||||
|
import { h, onMounted, ref } from 'vue'
|
||||||
|
import { addOrUpdateDictionary, deleteDictionary, getDictionaryList } from '@/service/api/dictionary'
|
||||||
|
import DictionaryTypeModal from './modules/DictionaryTypeModal.vue'
|
||||||
|
|
||||||
|
// 表格数据
|
||||||
|
const loading = ref(false)
|
||||||
|
const tableData = ref<Api.Dictionary.DictionaryItem[]>([])
|
||||||
|
|
||||||
|
// 字典类型模态框
|
||||||
|
const showTypeModal = ref(false)
|
||||||
|
const currentDictionaryId = ref(0)
|
||||||
|
|
||||||
|
function handleOpenTypeModal(row: Api.Dictionary.DictionaryItem) {
|
||||||
|
currentDictionaryId.value = row.Id
|
||||||
|
showTypeModal.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模态框控制
|
||||||
|
const showModal = ref(false)
|
||||||
|
const modalTitle = ref('新增字典')
|
||||||
|
const formRef = ref()
|
||||||
|
const formData = ref<Api.Dictionary.AddOrUpdateParams>({
|
||||||
|
Id: 0,
|
||||||
|
DicKey: '',
|
||||||
|
DicValue: '',
|
||||||
|
bakValue: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules = {
|
||||||
|
dicKey: { required: true, message: '请输入字典键', trigger: 'blur' },
|
||||||
|
dicValue: { required: true, message: '请输入字典值', trigger: 'blur' },
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
// 获取列表数据
|
||||||
|
async function fetchData() {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const { data: res, error } = await getDictionaryList()
|
||||||
|
if (!error && res) {
|
||||||
|
tableData.value = res.data || []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列定义
|
||||||
|
const columns: DataTableColumns<Api.Dictionary.DictionaryItem> = [
|
||||||
|
{ title: 'ID', key: 'Id' },
|
||||||
|
{
|
||||||
|
title: '字典键 (Key)',
|
||||||
|
key: 'DicKey',
|
||||||
|
render(row) {
|
||||||
|
return h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
text: true,
|
||||||
|
type: 'primary',
|
||||||
|
onClick: () => handleOpenTypeModal(row),
|
||||||
|
},
|
||||||
|
{ default: () => row.DicKey },
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ title: '字典值 (Value)', key: 'DicValue' },
|
||||||
|
{ title: '备注/备份值', key: 'BakValue' },
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'actions',
|
||||||
|
render(row) {
|
||||||
|
return h(NSpace, null, {
|
||||||
|
default: () => [
|
||||||
|
h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
size: 'small',
|
||||||
|
type: 'primary',
|
||||||
|
onClick: () => handleEdit(row),
|
||||||
|
},
|
||||||
|
{ default: () => '编辑' },
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
NPopconfirm,
|
||||||
|
{
|
||||||
|
onPositiveClick: () => handleDelete(row),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => '确认删除该条数据吗?',
|
||||||
|
trigger: () => h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
size: 'small',
|
||||||
|
type: 'error',
|
||||||
|
},
|
||||||
|
{ default: () => '删除' },
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
function handleAdd() {
|
||||||
|
modalTitle.value = '新增字典'
|
||||||
|
formData.value = {
|
||||||
|
Id: 0,
|
||||||
|
DicKey: '',
|
||||||
|
DicValue: '',
|
||||||
|
bakValue: '',
|
||||||
|
}
|
||||||
|
showModal.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
function handleEdit(row: Api.Dictionary.DictionaryItem) {
|
||||||
|
modalTitle.value = '编辑字典'
|
||||||
|
formData.value = { ...row }
|
||||||
|
showModal.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
async function handleDelete(row: Api.Dictionary.DictionaryItem) {
|
||||||
|
try {
|
||||||
|
await deleteDictionary(row.Id)
|
||||||
|
message.success('删除成功')
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
function handleSubmit() {
|
||||||
|
formRef.value?.validate(async (errors: any) => {
|
||||||
|
if (!errors) {
|
||||||
|
try {
|
||||||
|
await addOrUpdateDictionary(formData.value)
|
||||||
|
message.success(formData.value.Id ? '更新成功' : '新增成功')
|
||||||
|
showModal.value = false
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="h-full p-4">
|
||||||
|
<div class="mb-4 flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<!-- 这里可以放搜索框等 -->
|
||||||
|
</div>
|
||||||
|
<NButton type="primary" @click="handleAdd">
|
||||||
|
新增字典
|
||||||
|
</NButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<NDataTable
|
||||||
|
:columns="columns" :data="tableData" :loading="loading" :pagination="{ pageSize: 10 }"
|
||||||
|
:row-key="(row) => row.id" bordered class="h-[calc(100%-3rem)]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DictionaryTypeModal
|
||||||
|
v-model:visible="showTypeModal"
|
||||||
|
:dictionary-id="currentDictionaryId"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<NModal v-model:show="showModal" :title="modalTitle" preset="card" class="w-[600px]">
|
||||||
|
<NForm
|
||||||
|
ref="formRef" :model="formData" :rules="rules" label-placement="left" label-width="100"
|
||||||
|
require-mark-placement="right-hanging"
|
||||||
|
>
|
||||||
|
<NFormItem label="字典名称" path="DicKey">
|
||||||
|
<NInput v-model:value="formData.DicKey" placeholder="请输入字典名称" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="字典类型" path="DicValue">
|
||||||
|
<NInput v-model:value="formData.DicValue" placeholder="请输入字典值" type="textarea" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="备注" path="bakValue">
|
||||||
|
<NInput v-model:value="formData.bakValue" placeholder="请输入备注或备份值" />
|
||||||
|
</NFormItem>
|
||||||
|
</NForm>
|
||||||
|
<template #footer>
|
||||||
|
<NSpace justify="end">
|
||||||
|
<NButton @click="showModal = false">
|
||||||
|
取消
|
||||||
|
</NButton>
|
||||||
|
<NButton type="primary" @click="handleSubmit">
|
||||||
|
确定
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
</NModal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
176
apps/admin/src/views/dictionary/modules/DictionaryTypeModal.vue
Normal file
176
apps/admin/src/views/dictionary/modules/DictionaryTypeModal.vue
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { DataTableColumns } from 'naive-ui'
|
||||||
|
import { NButton, NDataTable, NForm, NFormItem, NInput, NModal, NPopconfirm, NSpace, useMessage } from 'naive-ui'
|
||||||
|
import { h, ref, watch } from 'vue'
|
||||||
|
import { addOrUpdateDictionaryUIType, deleteDictionaryItem, getDictionaryListUITypeByDicID } from '@/service/api/dictionary'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
visible: boolean
|
||||||
|
dictionaryId: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:visible', visible: boolean): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const tableData = ref<any[]>([])
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
// Modal control for Add/Edit UI Type
|
||||||
|
const showEditModal = ref(false)
|
||||||
|
const modalTitle = ref('新增字典类型')
|
||||||
|
const formRef = ref()
|
||||||
|
const formData = ref<Api.Dictionary.AddOrUpdateDictionaryUITypeParams>({
|
||||||
|
id: 0,
|
||||||
|
uI_Key: '',
|
||||||
|
uI_Value: '',
|
||||||
|
bakValue: '',
|
||||||
|
dictionaryID: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
uI_Key: { required: true, message: '请输入键', trigger: 'blur' },
|
||||||
|
uI_Value: { required: true, message: '请输入值', trigger: 'blur' },
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
if (!props.dictionaryId)
|
||||||
|
return
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const { data: res, error } = await getDictionaryListUITypeByDicID(props.dictionaryId)
|
||||||
|
if (!error && res) {
|
||||||
|
tableData.value = res.data || []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.visible, (val) => {
|
||||||
|
if (val) {
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const columns: DataTableColumns<any> = [
|
||||||
|
{ title: 'ID', key: 'Id', width: 80 },
|
||||||
|
{ title: '键 (Key)', key: 'UI_Key' },
|
||||||
|
{ title: '值 (Value)', key: 'UI_Value' },
|
||||||
|
{ title: '备注', key: 'BakValue' },
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'actions',
|
||||||
|
width: 150,
|
||||||
|
render(row) {
|
||||||
|
return h(NSpace, null, {
|
||||||
|
default: () => [
|
||||||
|
h(NButton, { size: 'small', type: 'primary', onClick: () => handleEdit(row) }, { default: () => '编辑' }),
|
||||||
|
h(
|
||||||
|
NPopconfirm,
|
||||||
|
{ onPositiveClick: () => handleDelete(row) },
|
||||||
|
{
|
||||||
|
default: () => '确认删除?',
|
||||||
|
trigger: () => h(NButton, { size: 'small', type: 'error' }, { default: () => '删除' }),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
modalTitle.value = '新增字典类型'
|
||||||
|
formData.value = {
|
||||||
|
id: 0,
|
||||||
|
uI_Key: '',
|
||||||
|
uI_Value: '',
|
||||||
|
bakValue: '',
|
||||||
|
dictionaryID: props.dictionaryId,
|
||||||
|
}
|
||||||
|
showEditModal.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(row: any) {
|
||||||
|
modalTitle.value = '编辑字典类型'
|
||||||
|
const { BakValue, ID, UI_Key, UI_Value } = row
|
||||||
|
formData.value = { ...row, bakValue: BakValue, id: ID, uI_Key: UI_Key, uI_Value: UI_Value, dictionaryID: props.dictionaryId }
|
||||||
|
showEditModal.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDelete(row: any) {
|
||||||
|
try {
|
||||||
|
const { error } = await deleteDictionaryItem(row.ID)
|
||||||
|
if (!error) {
|
||||||
|
message.success('删除成功')
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
formRef.value?.validate(async (errors: any) => {
|
||||||
|
if (!errors) {
|
||||||
|
try {
|
||||||
|
const { error } = await addOrUpdateDictionaryUIType(formData.value)
|
||||||
|
if (!error) {
|
||||||
|
message.success('操作成功')
|
||||||
|
showEditModal.value = false
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
emit('update:visible', false)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NModal :show="visible" preset="card" title="字典类型管理" class="w-[800px]" @update:show="handleClose">
|
||||||
|
<div class="mb-4">
|
||||||
|
<NButton type="primary" @click="handleAdd">
|
||||||
|
新增类型
|
||||||
|
</NButton>
|
||||||
|
</div>
|
||||||
|
<NDataTable :columns="columns" :data="tableData" :loading="loading" bordered :row-key="(row) => row.id" />
|
||||||
|
|
||||||
|
<NModal v-model:show="showEditModal" :title="modalTitle" preset="card" class="w-[500px]">
|
||||||
|
<NForm ref="formRef" :model="formData" :rules="rules" label-placement="left" label-width="80">
|
||||||
|
<NFormItem label="键" path="uI_Key">
|
||||||
|
<NInput v-model:value="formData.uI_Key" placeholder="请输入键" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="值" path="uI_Value">
|
||||||
|
<NInput v-model:value="formData.uI_Value" placeholder="请输入值" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="备注" path="bakValue">
|
||||||
|
<NInput v-model:value="formData.bakValue" placeholder="请输入备注" />
|
||||||
|
</NFormItem>
|
||||||
|
</NForm>
|
||||||
|
<template #footer>
|
||||||
|
<NSpace justify="end">
|
||||||
|
<NButton @click="showEditModal = false">
|
||||||
|
取消
|
||||||
|
</NButton>
|
||||||
|
<NButton type="primary" @click="handleSubmit">
|
||||||
|
确定
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
</NModal>
|
||||||
|
</NModal>
|
||||||
|
</template>
|
||||||
@ -50,10 +50,10 @@ function onMouseUp() {
|
|||||||
<div class="h-full flex overflow-hidden rounded-2xl bg-white shadow-sm">
|
<div class="h-full flex overflow-hidden rounded-2xl bg-white shadow-sm">
|
||||||
<!-- 分类树区域 -->
|
<!-- 分类树区域 -->
|
||||||
<div class="relative h-full flex-shrink-0" :style="{ width: `${siderWidth}px` }">
|
<div class="relative h-full flex-shrink-0" :style="{ width: `${siderWidth}px` }">
|
||||||
<CategoryTree @update:category="val => currentCategory = val" />
|
<CategoryTree @update:category="val => currentCategory = val" @drag-start="onMouseDown" />
|
||||||
<!-- 分类树宽度调整手柄 -->
|
<!-- 分类树宽度调整手柄 -->
|
||||||
<div
|
<div
|
||||||
class="absolute right-0 top-0 z-10 h-full w-1 cursor-col-resize transition-colors active:bg-primary/40 hover:bg-primary/20"
|
class="drag-handle absolute right-0 top-0 z-10 h-full w-1 cursor-col-resize transition-colors active:bg-primary/40 hover:bg-primary/20"
|
||||||
@mousedown="onMouseDown"
|
@mousedown="onMouseDown"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -10,12 +10,16 @@ import {
|
|||||||
} from 'naive-ui'
|
} from 'naive-ui'
|
||||||
import { onMounted, ref, watch } from 'vue'
|
import { onMounted, ref, watch } from 'vue'
|
||||||
import SvgIcon from '@/components/custom/svg-icon.vue'
|
import SvgIcon from '@/components/custom/svg-icon.vue'
|
||||||
|
import { useDict } from '@/hooks/business/useDict'
|
||||||
import { fetchAddQuestion, fetchDeleteQuestion, fetchGetQuestionListAll, fetchUpdateQuestion } from '@/service/api/question'
|
import { fetchAddQuestion, fetchDeleteQuestion, fetchGetQuestionListAll, fetchUpdateQuestion } from '@/service/api/question'
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update:category', node: any): void
|
(e: 'update:category', node: any): void
|
||||||
|
(e: 'dragStart', event: MouseEvent): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const { options: questionTypeOptions } = useDict('question_type')
|
||||||
|
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
|
|
||||||
@ -30,9 +34,8 @@ const categoryList = ref<CategoryItem[]>([])
|
|||||||
|
|
||||||
const selectedKey = ref<number | null>(null)
|
const selectedKey = ref<number | null>(null)
|
||||||
|
|
||||||
// Modal State
|
|
||||||
const showCategoryModal = ref(false)
|
const showCategoryModal = ref(false)
|
||||||
const categoryForm = ref({ questionContent: '', name: '', Id: 0 })
|
const categoryForm = ref({ questionContent: '', name: '', Id: 0, questionType: 'SingleChoice' })
|
||||||
const categoryOperation = ref<'add' | 'edit'>('add')
|
const categoryOperation = ref<'add' | 'edit'>('add')
|
||||||
const currentOperationNode = ref<CategoryItem | null>(null)
|
const currentOperationNode = ref<CategoryItem | null>(null)
|
||||||
|
|
||||||
@ -170,7 +173,7 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="h-full flex flex-col border-r border-gray-100 bg-gray-50/30">
|
<div class="drag-handle relative h-full flex flex-col border-r border-gray-100 bg-gray-50/30">
|
||||||
<div class="flex items-center justify-between border-b border-gray-100 px-4 py-4">
|
<div class="flex items-center justify-between border-b border-gray-100 px-4 py-4">
|
||||||
<span class="text-lg text-gray-700 font-bold">题目分类</span>
|
<span class="text-lg text-gray-700 font-bold">题目分类</span>
|
||||||
<NButton size="tiny" secondary type="primary" @click="handleAddCategory">
|
<NButton size="tiny" secondary type="primary" @click="handleAddCategory">
|
||||||
@ -227,9 +230,15 @@ onMounted(() => {
|
|||||||
class="w-[500px]"
|
class="w-[500px]"
|
||||||
>
|
>
|
||||||
<NForm>
|
<NForm>
|
||||||
|
<!-- 分类显示名称 -->
|
||||||
<NFormItem label="分类名称">
|
<NFormItem label="分类名称">
|
||||||
<NInput v-model:value="categoryForm.name" placeholder="请输入分类名称" @keyup.enter="submitCategory" />
|
<NInput v-model:value="categoryForm.name" placeholder="请输入分类名称" @keyup.enter="submitCategory" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
|
<!-- 题目类型 -->
|
||||||
|
<NFormItem label="题目类型">
|
||||||
|
<NSelect v-model:value="categoryForm.questionType" :options="questionTypeOptions" placeholder="请选择题目类型" />
|
||||||
|
</NFormItem>
|
||||||
|
<!-- 分类描述 -->
|
||||||
<NFormItem label="分类描述">
|
<NFormItem label="分类描述">
|
||||||
<NInput v-model:value="categoryForm.questionContent" placeholder="请输入分类描述" @keyup.enter="submitCategory" />
|
<NInput v-model:value="categoryForm.questionContent" placeholder="请输入分类描述" @keyup.enter="submitCategory" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
@ -245,9 +254,13 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</NModal>
|
</NModal>
|
||||||
|
|
||||||
|
<!-- 拖动把手图标 -->
|
||||||
|
<div
|
||||||
|
class="absolute right-0 top-1/2 z-50 h-8 w-4 flex translate-x-1/2 cursor-col-resize items-center justify-center border border-gray-200 rounded-full bg-white shadow-sm transition-all -translate-y-1/2 hover:scale-110 hover:bg-gray-50"
|
||||||
|
@mousedown="(e) => emit('dragStart', e)"
|
||||||
|
>
|
||||||
|
<SvgIcon icon="carbon:draggable" class="text-[10px] text-gray-400" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
/* No specific styles needed as we use Tailwind classes */
|
|
||||||
</style>
|
|
||||||
|
|||||||
@ -11,6 +11,9 @@
|
|||||||
"prepare": "simple-git-hooks",
|
"prepare": "simple-git-hooks",
|
||||||
"typecheck": "pnpm -r typecheck"
|
"typecheck": "pnpm -r typecheck"
|
||||||
},
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"pinia-plugin-persistedstate": "^4.7.1"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@read-star/eslint-config": "workspace:*",
|
"@read-star/eslint-config": "workspace:*",
|
||||||
"eslint": "latest",
|
"eslint": "latest",
|
||||||
|
|||||||
24
pnpm-lock.yaml
generated
24
pnpm-lock.yaml
generated
@ -7,6 +7,10 @@ settings:
|
|||||||
importers:
|
importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
|
dependencies:
|
||||||
|
pinia-plugin-persistedstate:
|
||||||
|
specifier: ^4.7.1
|
||||||
|
version: 4.7.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.26(typescript@5.9.3)))
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@read-star/eslint-config':
|
'@read-star/eslint-config':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
@ -4064,6 +4068,20 @@ packages:
|
|||||||
engines: {node: '>=0.10'}
|
engines: {node: '>=0.10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
pinia-plugin-persistedstate@4.7.1:
|
||||||
|
resolution: {integrity: sha512-WHOqh2esDlR3eAaknPbqXrkkj0D24h8shrDPqysgCFR6ghqP/fpFfJmMPJp0gETHsvrh9YNNg6dQfo2OEtDnIQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@nuxt/kit': '>=3.0.0'
|
||||||
|
'@pinia/nuxt': '>=0.10.0'
|
||||||
|
pinia: '>=3.0.0'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@nuxt/kit':
|
||||||
|
optional: true
|
||||||
|
'@pinia/nuxt':
|
||||||
|
optional: true
|
||||||
|
pinia:
|
||||||
|
optional: true
|
||||||
|
|
||||||
pinia@3.0.4:
|
pinia@3.0.4:
|
||||||
resolution: {integrity: sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==}
|
resolution: {integrity: sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -9362,6 +9380,12 @@ snapshots:
|
|||||||
|
|
||||||
pidtree@0.6.0: {}
|
pidtree@0.6.0: {}
|
||||||
|
|
||||||
|
pinia-plugin-persistedstate@4.7.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.26(typescript@5.9.3))):
|
||||||
|
dependencies:
|
||||||
|
defu: 6.1.4
|
||||||
|
optionalDependencies:
|
||||||
|
pinia: 3.0.4(typescript@5.9.3)(vue@3.5.26(typescript@5.9.3))
|
||||||
|
|
||||||
pinia@3.0.4(typescript@5.9.3)(vue@3.5.26(typescript@5.9.3)):
|
pinia@3.0.4(typescript@5.9.3)(vue@3.5.26(typescript@5.9.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/devtools-api': 7.7.9
|
'@vue/devtools-api': 7.7.9
|
||||||
|
|||||||
Reference in New Issue
Block a user