xiongzhu 1 an în urmă
părinte
comite
03741ab64d

+ 12 - 2
src/components/EditDialog.vue

@@ -7,7 +7,14 @@
         :close-on-press-escape="!saving"
         :width="width"
     >
-        <ElForm :model="model" :rules="rules" ref="formEl" label-position="right" :label-width="labelWidth">
+        <ElForm
+            :model="model"
+            :rules="rules"
+            ref="formEl"
+            label-position="right"
+            :label-width="labelWidth"
+            v-bind="$attrs"
+        >
             <slot></slot>
         </ElForm>
         <div class="text-right">
@@ -17,8 +24,11 @@
     </ElDialog>
 </template>
 <script setup>
-import { computed, ref, watch } from 'vue'
+import { computed, ref, watch, defineOptions } from 'vue'
 import { ElMessage } from 'element-plus'
+defineOptions({
+    inheritAttrs: false
+})
 const props = defineProps({
     modelValue: {
         type: Boolean,

+ 22 - 0
src/components/ExpandItem.vue

@@ -0,0 +1,22 @@
+<template>
+    <div class="expand-item">
+        <div class="key prelin">
+            <slot name="title"></slot>
+        </div>
+        <div class="value whitespace-pre-line">
+            <slot name="content"></slot>
+        </div>
+    </div>
+</template>
+<script setup></script>
+<style lang="less" scoped>
+.expand-item {
+    min-width: 160px;
+    margin-bottom: 20px;
+    .key {
+    }
+    .value {
+        color: var(--el-color-primary);
+    }
+}
+</style>

+ 8 - 4
src/components/PagingTable.vue

@@ -3,7 +3,7 @@
         <slot name="filter"></slot>
     </div>
     <ElConfigProvider :size="isMobile ? '' : 'small'">
-        <ElTable :data="tableData" :height="height || tableHeight" stripe v-loading="loading">
+        <ElTable :data="tableData" :height="height || tableHeight" stripe v-loading="loading" v-bind="$attrs" ref="tableEl">
             <slot></slot>
         </ElTable>
     </ElConfigProvider>
@@ -19,11 +19,13 @@
     </div>
 </template>
 <script setup>
-import { ref, onMounted, computed, watch, inject } from 'vue'
+import { ref, onMounted, computed, watch, inject, defineOptions } from 'vue'
 import { http } from '@/plugins/http'
 import { ElMessage } from 'element-plus'
 import { useStorage, useElementBounding, useWindowSize } from '@vueuse/core'
-
+defineOptions({
+    inheritAttrs: false
+})
 const props = defineProps({
     url: {
         type: String,
@@ -54,6 +56,7 @@ const search = computed(() => {
 
 const filterEl = ref(null)
 const paginEl = ref(null)
+const tableEl = ref(null)
 const { height: filterHeight } = useElementBounding(filterEl)
 const { height: paginHeight } = useElementBounding(paginEl)
 const { height: windowHeight } = useWindowSize()
@@ -104,7 +107,8 @@ watch([page, pageConfig], () => {
 defineExpose({
     refresh() {
         getData()
-    }
+    },
+    tableEl
 })
 </script>
 <style lang="less" scoped>

+ 6 - 10
src/views/PhoneListView.vue

@@ -4,14 +4,14 @@
             <ElButton :icon="Plus" @click="onEdit()">添加</ElButton>
         </template>
         <ElTableColumn prop="id" label="#" width="80" />
-        <ElTableColumn prop="name" label="名称" min-width="120" />
+        <ElTableColumn prop="name" label="名称" show-overflow-tooltip />
         <ElTableColumn prop="remark" label="备注" show-overflow-tooltip />
         <ElTableColumn prop="createdAt" label="创建时间" :formatter="timeFormatter" width="150" />
         <ElTableColumn label="操作" align="center" width="200">
             <template #default="{ row }">
                 <ElButton type="primary" size="small" @click="detail(row)">详情</ElButton>
                 <ElButton type="danger" size="small" @click="del(row)">删除</ElButton>
-                <ElButton @click="download(row)" v-if="shouldShow">下载</ElButton>
+                <ElButton @click="download(row)" v-if="isAdmin">下载</ElButton>
             </template>
         </ElTableColumn>
     </PagingTable>
@@ -28,7 +28,7 @@
         <PagingTable url="/phone-list/phone" :where="{ listId: (selectedRow || {}).id }" ref="phoneTable" height="50vh">
             <template #filter>
                 <ElButton :icon="Plus" @click="onPhoneEdit()">添加</ElButton>
-                <ElButton :icon="Plus" @click="importList()" >导入</ElButton>
+                <ElButton :icon="Plus" @click="importList()">导入</ElButton>
             </template>
             <ElTableColumn prop="id" label="#" width="80" />
             <ElTableColumn prop="number" label="号码" min-width="120" />
@@ -54,7 +54,7 @@
     </EditDialog>
 </template>
 <script setup>
-import { ref } from 'vue'
+import { inject, ref } from 'vue'
 import PagingTable from '@/components/PagingTable.vue'
 import { useTimeFormatter } from '@/utils/formatter'
 import { Plus } from '@vicons/tabler'
@@ -67,12 +67,8 @@ import { ElLoading, ElMessage, ElMessageBox } from 'element-plus'
 import { useClipboard } from '@vueuse/core'
 import { useUserStore } from '@/stores/user'
 
-const { user } = useUserStore()
+const isAdmin = inject('isAdmin')
 const where = ref({})
-const roles = user.roles
-if (roles.includes('user')) {
-    where.value.userId = user.id
-}
 const timeFormatter = useTimeFormatter()
 const table = ref(null)
 const model = ref({})
@@ -132,7 +128,7 @@ function importList() {
         const loading = ElLoading.service({
             lock: true,
             text: '导入中',
-            background: 'rgba(0, 0, 0, 0.7)',
+            background: 'rgba(0, 0, 0, 0.7)'
         })
         const file = input.files[0]
         const formData = new FormData()

+ 209 - 64
src/views/TaskView.vue

@@ -1,20 +1,108 @@
 <template>
-    <PagingTable url="/task" :where="where" ref="table">
+    <PagingTable url="/task" :where="where" ref="table" @row-click="rowClick" :stripe="false">
         <template #filter>
             <ElButton :icon="Refresh" @click="table.refresh()"></ElButton>
             <ElButton :icon="Plus" @click="onEdit({ checkConnection: true }), getPhoneList()"> 添加</ElButton>
         </template>
+        <ElTableColumn type="expand">
+            <template #default="{ row }">
+                <div class="px-4 py-2">
+                    <div class="bg-neutral-100 dark:bg-neutral-800 rounded-md p-4">
+                        <div class="flex flex-wrap">
+                            <ExpandItem class="w-full">
+                                <template #title>发送内容</template>
+                                <template #content>{{ row.message }}</template>
+                            </ExpandItem>
+
+                            <ExpandItem class="w-full" v-if="row.dynamicMessage && row.dynamicMessage.length">
+                                <template #title>动态内容</template>
+                                <template #content>
+                                    <div v-for="(dm, i) in row.dynamicMessage" :key="i">
+                                        <div>
+                                            <span>{{ dm.key }}: </span>
+                                            <span>{{ dm.values.join(', ') }}</span>
+                                        </div>
+                                    </div>
+                                </template>
+                            </ExpandItem>
+
+                            <ExpandItem v-if="isAdmin">
+                                <template #title>RCS等待时间</template>
+                                <template #content>{{ row.rcsWait }}</template>
+                            </ExpandItem>
+
+                            <ExpandItem class="expand-item" v-if="isAdmin">
+                                <template #title>RCS发送间隔</template>
+                                <template #content>{{ row.rcsInterval }}</template>
+                            </ExpandItem>
+
+                            <ExpandItem class="expand-item" v-if="isAdmin">
+                                <template #title>清理数量</template>
+                                <template #content>{{ row.cleanCount }}</template>
+                            </ExpandItem>
+
+                            <ExpandItem class="expand-item" v-if="isAdmin">
+                                <template #title>请求号码间隔</template>
+                                <template #content>{{ row.requestNumberInterval }}</template>
+                            </ExpandItem>
+
+                            <ExpandItem class="expand-item" v-if="isAdmin">
+                                <template #title>检查连接</template>
+                                <template #content>{{ row.checkConnection ? '是' : '否' }}</template>
+                            </ExpandItem>
+                        </div>
+
+                        <div>
+                            <div>
+                                <ElButton
+                                    type="primary"
+                                    size="small"
+                                    @click="getToBeSentNum()"
+                                    v-if="row.status === 'queued'"
+                                >
+                                    排队状态
+                                </ElButton>
+                                <ElButton type="primary" size="small" @click="detail(row)">详情</ElButton>
+                                <ElButton type="primary" size="small" @click="onEdit(row)" v-if="isAdmin">
+                                    编辑
+                                </ElButton>
+                                <ElButton
+                                    type="primary"
+                                    size="small"
+                                    @click="start(row)"
+                                    v-if="row.status === 'idle' || row.status === 'pause'"
+                                >
+                                    开始
+                                </ElButton>
+                                <ElButton
+                                    type="primary"
+                                    size="small"
+                                    @click="pause(row)"
+                                    v-if="isAdmin && row.status === 'pending'"
+                                >
+                                    暂停
+                                </ElButton>
+                                <ElButton
+                                    type="primary"
+                                    size="small"
+                                    @click="receipt(row)"
+                                    v-if="row.status === 'completed'"
+                                >
+                                    回执
+                                </ElButton>
+                                <ElButton type="danger" size="small" @click="del(row)" v-if="row.status === 'idle'">
+                                    删除
+                                </ElButton>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </template>
+        </ElTableColumn>
         <ElTableColumn prop="id" label="#" width="80" />
-        <ElTableColumn prop="name" label="名称" min-width="120" />
+        <ElTableColumn prop="name" label="名称" show-overflow-tooltip />
         <ElTableColumn prop="remark" label="备注" show-overflow-tooltip />
-        <ElTableColumn prop="message" label="内容" show-overflow-tooltip />
-        <ElTableColumn prop="dynamicMessage" label="动态内容" show-overflow-tooltip />
         <ElTableColumn prop="listId" label="发送列表" :formatter="phoneListFormatter" />
-        <ElTableColumn v-if="isAdmin" prop="rcsWait" label="RCS等待时间" />
-        <ElTableColumn v-if="isAdmin" prop="rcsInterval" label="RCS发送间隔" />
-        <ElTableColumn v-if="isAdmin" prop="cleanCount" label="清理数量" />
-        <ElTableColumn v-if="isAdmin" prop="requestNumberInterval" label="请求号码间隔" />
-        <ElTableColumn v-if="isAdmin" prop="checkConnection" label="检查连接" />
         <ElTableColumn prop="status" label="状态" align="center">
             <template #default="{ row }">
                 <ElTag v-if="row.status === 'idle'" type="info">未发送</ElTag>
@@ -26,40 +114,22 @@
                 <ElTag v-else>未知</ElTag>
             </template>
         </ElTableColumn>
-        <ElTableColumn prop="sent" label="已发送" align="center" />
+        <ElTableColumn prop="sent" label="已发送" align="center">
+            <template #default="{ row }"> {{ row.sent || 0 }} / {{ row.total || 0 }} </template>
+        </ElTableColumn>
         <ElTableColumn prop="successRate" label="发送成功率" align="center" />
         <ElTableColumn prop="createdAt" label="创建时间" :formatter="timeFormatter" width="150" />
-        <ElTableColumn label="操作" align="center" width="200">
-            <template #default="{ row }">
-                <ElButton type="primary" size="small" @click="getToBeSentNum()" v-if="row.status === 'queued'">
-                    排队状态
-                </ElButton>
-                <ElButton type="primary" size="small" @click="detail(row)">详情</ElButton>
-                <ElButton
-                    type="primary"
-                    size="small"
-                    @click="start(row)"
-                    v-if="row.status === 'idle' || row.status === 'pause'"
-                >
-                    开始
-                </ElButton>
-                <ElButton type="primary" size="small" @click="pause(row)" v-if="isAdmin && row.status === 'pending'">
-                    暂停
-                </ElButton>
-                <ElButton type="primary" size="small" @click="receipt(row)" v-if="row.status === 'completed'">
-                    回执
-                </ElButton>
-                <ElButton type="danger" size="small" @click="del(row)" v-if="row.status === 'idle'">删除</ElButton>
-            </template>
-        </ElTableColumn>
     </PagingTable>
     <EditDialog
+        class="task-edit-dialog"
         v-model="showEditDialog"
         :model="model"
         :rules="rules"
         :on-submit="submit"
         @success="table.refresh()"
         label-width="120px"
+        :title="`${model.id ? '编辑' : '新建'}任务`"
+        inline
     >
         <ElFormItem prop="name" label="名称">
             <ElInput v-model="model.name" placeholder="请输入名称" />
@@ -68,12 +138,18 @@
             <ElInput v-model="model.remark" placeholder="请输入备注" />
         </ElFormItem>
         <ElFormItem prop="listId" label="发送列表">
-            <ElSelect v-model="model.listId" placeholder="请选择发送列表">
+            <ElSelect v-model="model.listId" placeholder="请选择发送列表" :disabled="!!model.id">
                 <ElOption v-for="item in phoneList" :key="item.id" :label="item.name" :value="item.id" />
             </ElSelect>
         </ElFormItem>
-        <ElFormItem v-if="isAdmin" prop="channelId" label="渠道列表">
-            <ElSelect v-model="model.channelId" multiple placeholder="请选择渠道列表">
+        <ElFormItem v-if="isAdmin" prop="channels" label="渠道列表">
+            <ElSelect
+                v-model="model.channels"
+                multiple
+                placeholder="请选择渠道列表"
+                :collapse-tags="true"
+                :collapse-tags-tooltip="true"
+            >
                 <ElOption
                     v-for="item in operatorList"
                     :key="item.id"
@@ -82,34 +158,54 @@
                 />
             </ElSelect>
         </ElFormItem>
-        <ElFormItem prop="message" label="内容">
+        <ElFormItem prop="message" label="内容" v-if="!model.id" class="!w-full">
             <ElInput v-model="model.message" placeholder="请输入内容" type="textarea" />
         </ElFormItem>
-        <ElFormItem prop="dynamicMessage" label="动态内容">
-            <ElInput v-model="model.dynamicMessage" placeholder="请输入动态内容" type="textarea" />
+        <ElFormItem prop="dynamicMessage" label="动态内容" v-if="!model.id" class="!w-full">
+            <div
+                v-for="(dm, i) in model.dynamicMessage"
+                :key="i"
+                class="bg-neutral-100 dark:bg-neutral-900 p-3 rounded-md mb-4 w-full"
+            >
+                <div></div>
+                <div>
+                    <ElInput v-model="dm.key" placeholder="关键字" class="mb-2">
+                        <template #append>
+                            <ElButton :icon="Trash" @click="model.dynamicMessage.splice(i, 1)" />
+                        </template>
+                    </ElInput>
+                </div>
+                <ElInput v-model="dm.values" placeholder="替换内容" type="textarea" />
+            </div>
+            <ElButton
+                @click="
+                    ;(model.dynamicMessage = model.dynamicMessage || []),
+                        model.dynamicMessage.push({
+                            key: '',
+                            values: ''
+                        })
+                "
+                :icon="Plus"
+            />
         </ElFormItem>
         <ElFormItem v-if="isAdmin" prop="rcsWait" label="RCS等待时间">
             <ElInputNumber :controls="false" v-model="model.rcsWait" placeholder="请输入RCS等待时间" />
-            <div class="tip">(0表示使用系统默认值)</div>
         </ElFormItem>
         <ElFormItem v-if="isAdmin" prop="rcsInterval" label="RCS发送间隔">
             <ElInputNumber :controls="false" v-model="model.rcsInterval" placeholder="请输入RCS发送间隔" />
-            <div class="tip">(0表示使用系统默认值)</div>
         </ElFormItem>
         <ElFormItem v-if="isAdmin" prop="cleanCount" label="清理数量">
             <ElInputNumber :controls="false" v-model="model.cleanCount" placeholder="请输入清理数量" />
-            <div class="tip">(0表示使用系统默认值)</div>
         </ElFormItem>
         <ElFormItem v-if="isAdmin" prop="requestNumberInterval" label="请求号码间隔">
             <ElInputNumber :controls="false" v-model="model.requestNumberInterval" placeholder="请输入请求号码间隔" />
-            <div class="tip">(0表示使用系统默认值)</div>
-        </ElFormItem>
-        <ElFormItem v-if="isAdmin" prop="checkConnection" label="检查连接">
-            <ElSwitch v-model="model.checkConnection" />
         </ElFormItem>
         <ElFormItem v-if="isAdmin" prop="matchDevice" label="筛选设备">
             <ElInput v-model="model.matchDevice" placeholder="设备名称" />
         </ElFormItem>
+        <ElFormItem v-if="isAdmin" prop="checkConnection" label="检查连接">
+            <ElSwitch v-model="model.checkConnection" />
+        </ElFormItem>
     </EditDialog>
 
     <ElDialog v-model="showDetailDialog" title="详情" width="800px">
@@ -131,14 +227,27 @@
     </ElDialog>
 
     <EditDialog
-        v-model="showPhoneEditDialog"
-        :model="phoneModel"
-        :rules="phoneRules"
-        :on-submit="submitPhone"
-        @success="phoneTable.refresh()"
+        v-model="showDmDialog"
+        :model="dmModel"
+        @success="table.refresh()"
+        label-width="120px"
+        title="编辑动态内容"
     >
-        <ElFormItem prop="number" label="号码">
-            <ElInput v-model="phoneModel.number" placeholder="号码" />
+        <ElFormItem prop="key" label="关键字">
+            <ElInput v-model="dmModel.key" placeholder="请输入需要替换的关键字" />
+        </ElFormItem>
+        <ElFormItem prop="values" label="替换内容">
+            <ElInput
+                v-for="(item, index) in dmModel.values"
+                v-model="dmModel.values[index]"
+                :key="index"
+                placeholder="请输入替换内容"
+            >
+                <template #append>
+                    <ElButton @click="delValue(index)" :icon="Trash"></ElButton>
+                </template>
+            </ElInput>
+            <ElButton @click="addValue" :icon="Plus"></ElButton>
         </ElFormItem>
     </EditDialog>
 </template>
@@ -146,13 +255,14 @@
 import { computed, inject, ref } from 'vue'
 import PagingTable from '@/components/PagingTable.vue'
 import { useTimeFormatter } from '@/utils/formatter'
-import { Plus, Refresh } from '@vicons/tabler'
+import { Plus, Refresh, Trash } from '@vicons/tabler'
 import EditDialog from '@/components/EditDialog.vue'
 import { setupEditDialog } from '@/utils/editDialog'
 import { http } from '@/plugins/http'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { storeToRefs } from 'pinia'
 import { useUserStore } from '@/stores/user'
+import ExpandItem from '@/components/ExpandItem.vue'
 
 const { user } = storeToRefs(useUserStore())
 const where = ref({})
@@ -179,12 +289,21 @@ const rules = {
 }
 const { showEditDialog, onEdit } = setupEditDialog(model)
 
+function rowClick(row, column) {
+    table.value.tableEl.toggleRowExpansion(row)
+}
+
 async function submit() {
-    if (model.value.channelId) {
-        model.value.channelId = model.value.channelId.join(',')
-    }
-    const result = await http.put('/task', {
+    const result = await http.put(model.value.id ? `/task/${model.value.id}` : '/task', {
         ...model.value,
+        dynamicMessage:
+            model.value.dynamicMessage?.map((item) => ({
+                key: item.key,
+                values: item.values
+                    .split('\n')
+                    .map((v) => v.trim())
+                    .filter((v) => !!v)
+            })) || [],
         userId: user.value.id
     })
     if (result) {
@@ -237,12 +356,6 @@ function detail(row) {
     showDetailDialog.value = true
 }
 
-const phoneModel = ref({})
-const phoneRules = {
-    number: [{ required: true, message: '请输入号码', trigger: 'blur' }]
-}
-const { showEditDialog: showPhoneEditDialog, onEdit: onPhoneEdit } = setupEditDialog(phoneModel)
-
 function taskStatusFormatter(row, column, cellValue, index) {
     switch (cellValue) {
         case 'idle':
@@ -336,6 +449,25 @@ async function receipt(row) {
         ElMessage.error('获取回执错误,请稍后再试.')
     }
 }
+
+const dmModel = ref({
+    key: '',
+    values: []
+})
+const showDmDialog = ref(false)
+function editDm(row) {
+    dmModel.value = row || {
+        key: '',
+        values: []
+    }
+    showDmDialog.value = true
+}
+function addValue() {
+    dmModel.value.values.push('')
+}
+function delValue(index) {
+    dmModel.value.values.splice(index, 1)
+}
 </script>
 <style lang="less" scoped>
 .tip {
@@ -343,4 +475,17 @@ async function receipt(row) {
     font-size: 12px;
     margin-left: 10px;
 }
+.task-edit-dialog {
+    .el-form-item {
+        width: 50%;
+        margin-right: 0;
+        padding-left: 20px;
+        vertical-align: top;
+    }
+    .el-input,
+    .el-input-number,
+    .el-select {
+        width: 100% !important;
+    }
+}
 </style>

+ 134 - 118
yarn.lock

@@ -7,10 +7,10 @@
   resolved "https://registry.npmmirror.com/@antfu/utils/-/utils-0.7.2.tgz#3bb6f37a6b188056fe9e2f363b6aa735ed65d7ca"
   integrity sha512-vy9fM3pIxZmX07dL+VX1aZe7ynZ+YyB0jY+jE6r3hOK6GNY2t6W8rzpFC4tgpbXUYABkFQwgJq2XYXlxbXAI0g==
 
-"@babel/parser@^7.16.4":
-  version "7.21.4"
-  resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17"
-  integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==
+"@babel/parser@^7.24.7":
+  version "7.24.7"
+  resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.24.7.tgz#9a5226f92f0c5c8ead550b750f5608e766c8ce85"
+  integrity sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==
 
 "@ctrl/tinycolor@^3.4.1":
   version "3.6.0"
@@ -219,7 +219,7 @@
   resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
   integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
 
-"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13":
+"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@^1.4.15":
   version "1.4.15"
   resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
   integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
@@ -314,47 +314,47 @@
   resolved "https://registry.npmmirror.com/@volar-plugins/prettier/-/prettier-2.0.0.tgz#9fe8dce0207329de0aee896055c3d74f991a040a"
   integrity sha512-TRJJrsqzDceEHlM/nL5waaadzBpJHtI/Au83faULXGgK5Hcc8GYuHzDJzOvCyvXgX/pWL6Z+NYgwHbh3TW3UMg==
 
-"@vue/compiler-core@3.2.47":
-  version "3.2.47"
-  resolved "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.47.tgz#3e07c684d74897ac9aa5922c520741f3029267f8"
-  integrity sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==
+"@vue/compiler-core@3.4.31":
+  version "3.4.31"
+  resolved "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.4.31.tgz#b51a76f1b30e9b5eba0553264dff0f171aedb7c6"
+  integrity sha512-skOiodXWTV3DxfDhB4rOf3OGalpITLlgCeOwb+Y9GJpfQ8ErigdBUHomBzvG78JoVE8MJoQsb+qhZiHfKeNeEg==
   dependencies:
-    "@babel/parser" "^7.16.4"
-    "@vue/shared" "3.2.47"
+    "@babel/parser" "^7.24.7"
+    "@vue/shared" "3.4.31"
+    entities "^4.5.0"
     estree-walker "^2.0.2"
-    source-map "^0.6.1"
-
-"@vue/compiler-dom@3.2.47":
-  version "3.2.47"
-  resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz#a0b06caf7ef7056939e563dcaa9cbde30794f305"
-  integrity sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==
-  dependencies:
-    "@vue/compiler-core" "3.2.47"
-    "@vue/shared" "3.2.47"
-
-"@vue/compiler-sfc@3.2.47":
-  version "3.2.47"
-  resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz#1bdc36f6cdc1643f72e2c397eb1a398f5004ad3d"
-  integrity sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==
-  dependencies:
-    "@babel/parser" "^7.16.4"
-    "@vue/compiler-core" "3.2.47"
-    "@vue/compiler-dom" "3.2.47"
-    "@vue/compiler-ssr" "3.2.47"
-    "@vue/reactivity-transform" "3.2.47"
-    "@vue/shared" "3.2.47"
+    source-map-js "^1.2.0"
+
+"@vue/compiler-dom@3.4.31":
+  version "3.4.31"
+  resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.4.31.tgz#30961ca847f5d6ad18ffa26236c219f61b195f6b"
+  integrity sha512-wK424WMXsG1IGMyDGyLqB+TbmEBFM78hIsOJ9QwUVLGrcSk0ak6zYty7Pj8ftm7nEtdU/DGQxAXp0/lM/2cEpQ==
+  dependencies:
+    "@vue/compiler-core" "3.4.31"
+    "@vue/shared" "3.4.31"
+
+"@vue/compiler-sfc@3.4.31":
+  version "3.4.31"
+  resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.4.31.tgz#cc6bfccda17df8268cc5440842277f61623c591f"
+  integrity sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ==
+  dependencies:
+    "@babel/parser" "^7.24.7"
+    "@vue/compiler-core" "3.4.31"
+    "@vue/compiler-dom" "3.4.31"
+    "@vue/compiler-ssr" "3.4.31"
+    "@vue/shared" "3.4.31"
     estree-walker "^2.0.2"
-    magic-string "^0.25.7"
-    postcss "^8.1.10"
-    source-map "^0.6.1"
+    magic-string "^0.30.10"
+    postcss "^8.4.38"
+    source-map-js "^1.2.0"
 
-"@vue/compiler-ssr@3.2.47":
-  version "3.2.47"
-  resolved "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz#35872c01a273aac4d6070ab9d8da918ab13057ee"
-  integrity sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==
+"@vue/compiler-ssr@3.4.31":
+  version "3.4.31"
+  resolved "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.4.31.tgz#f62ffecdf15bacb883d0099780cf9a1e3654bfc4"
+  integrity sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA==
   dependencies:
-    "@vue/compiler-dom" "3.2.47"
-    "@vue/shared" "3.2.47"
+    "@vue/compiler-dom" "3.4.31"
+    "@vue/shared" "3.4.31"
 
 "@vue/devtools-api@^6.4.5", "@vue/devtools-api@^6.5.0":
   version "6.5.0"
@@ -369,53 +369,43 @@
     eslint-config-prettier "^8.3.0"
     eslint-plugin-prettier "^4.0.0"
 
-"@vue/reactivity-transform@3.2.47":
-  version "3.2.47"
-  resolved "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz#e45df4d06370f8abf29081a16afd25cffba6d84e"
-  integrity sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==
+"@vue/reactivity@3.4.31":
+  version "3.4.31"
+  resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.4.31.tgz#eda80e90c4f9d7659efe1f5ed99c2dfdc9e93d77"
+  integrity sha512-VGkTani8SOoVkZNds1PfJ/T1SlAIOf8E58PGAhIOUDYPC4GAmFA2u/E14TDAFcf3vVDKunc4QqCe/SHr8xC65Q==
   dependencies:
-    "@babel/parser" "^7.16.4"
-    "@vue/compiler-core" "3.2.47"
-    "@vue/shared" "3.2.47"
-    estree-walker "^2.0.2"
-    magic-string "^0.25.7"
-
-"@vue/reactivity@3.2.47":
-  version "3.2.47"
-  resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.47.tgz#1d6399074eadfc3ed35c727e2fd707d6881140b6"
-  integrity sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==
-  dependencies:
-    "@vue/shared" "3.2.47"
+    "@vue/shared" "3.4.31"
 
-"@vue/runtime-core@3.2.47":
-  version "3.2.47"
-  resolved "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.47.tgz#406ebade3d5551c00fc6409bbc1eeb10f32e121d"
-  integrity sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==
+"@vue/runtime-core@3.4.31":
+  version "3.4.31"
+  resolved "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.4.31.tgz#ad3a41ad76385c0429e3e4dbefb81918494e10cf"
+  integrity sha512-LDkztxeUPazxG/p8c5JDDKPfkCDBkkiNLVNf7XZIUnJ+66GVGkP+TIh34+8LtPisZ+HMWl2zqhIw0xN5MwU1cw==
   dependencies:
-    "@vue/reactivity" "3.2.47"
-    "@vue/shared" "3.2.47"
+    "@vue/reactivity" "3.4.31"
+    "@vue/shared" "3.4.31"
 
-"@vue/runtime-dom@3.2.47":
-  version "3.2.47"
-  resolved "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz#93e760eeaeab84dedfb7c3eaf3ed58d776299382"
-  integrity sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==
+"@vue/runtime-dom@3.4.31":
+  version "3.4.31"
+  resolved "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.4.31.tgz#bae7ad844f944af33699c73581bc36125bab96ce"
+  integrity sha512-2Auws3mB7+lHhTFCg8E9ZWopA6Q6L455EcU7bzcQ4x6Dn4cCPuqj6S2oBZgN2a8vJRS/LSYYxwFFq2Hlx3Fsaw==
   dependencies:
-    "@vue/runtime-core" "3.2.47"
-    "@vue/shared" "3.2.47"
-    csstype "^2.6.8"
+    "@vue/reactivity" "3.4.31"
+    "@vue/runtime-core" "3.4.31"
+    "@vue/shared" "3.4.31"
+    csstype "^3.1.3"
 
-"@vue/server-renderer@3.2.47":
-  version "3.2.47"
-  resolved "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.47.tgz#8aa1d1871fc4eb5a7851aa7f741f8f700e6de3c0"
-  integrity sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==
+"@vue/server-renderer@3.4.31":
+  version "3.4.31"
+  resolved "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.4.31.tgz#bbe990f793c36d62d05bdbbaf142511d53e159fd"
+  integrity sha512-D5BLbdvrlR9PE3by9GaUp1gQXlCNadIZytMIb8H2h3FMWJd4oUfkUTEH2wAr3qxoRz25uxbTcbqd3WKlm9EHQA==
   dependencies:
-    "@vue/compiler-ssr" "3.2.47"
-    "@vue/shared" "3.2.47"
+    "@vue/compiler-ssr" "3.4.31"
+    "@vue/shared" "3.4.31"
 
-"@vue/shared@3.2.47":
-  version "3.2.47"
-  resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.47.tgz#e597ef75086c6e896ff5478a6bfc0a7aa4bbd14c"
-  integrity sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==
+"@vue/shared@3.4.31":
+  version "3.4.31"
+  resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.4.31.tgz#af9981f57def2c3f080c14bf219314fc0dc808a0"
+  integrity sha512-Yp3wtJk//8cO4NItOPpi3QkLExAr/aLBGZMmTtW9WpdwBCJpRM6zj9WgWktXAl8IDIozwNMByT45JP3tO3ACWA==
 
 "@vueuse/core@^10.1.0":
   version "10.1.0"
@@ -699,10 +689,10 @@ cssesc@^3.0.0:
   resolved "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
   integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
 
-csstype@^2.6.8:
-  version "2.6.21"
-  resolved "https://registry.npmmirror.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e"
-  integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==
+csstype@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
+  integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
 
 date-fns@^2.29.3:
   version "2.29.3"
@@ -802,6 +792,11 @@ engine.io-parser@~5.2.1:
   resolved "https://registry.npmmirror.com/engine.io-parser/-/engine.io-parser-5.2.1.tgz#9f213c77512ff1a6cc0c7a86108a7ffceb16fcfb"
   integrity sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==
 
+entities@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
+  integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
+
 errno@^0.1.1:
   version "0.1.8"
   resolved "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
@@ -1024,6 +1019,11 @@ file-entry-cache@^6.0.1:
   dependencies:
     flat-cache "^3.0.4"
 
+file-saver@^2.0.5:
+  version "2.0.5"
+  resolved "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
+  integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
+
 fill-range@^7.0.1:
   version "7.0.1"
   resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
@@ -1369,13 +1369,6 @@ lru-cache@^6.0.0:
   dependencies:
     yallist "^4.0.0"
 
-magic-string@^0.25.7:
-  version "0.25.9"
-  resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
-  integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
-  dependencies:
-    sourcemap-codec "^1.4.8"
-
 magic-string@^0.30.0:
   version "0.30.0"
   resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.0.tgz#fd58a4748c5c4547338a424e90fa5dd17f4de529"
@@ -1383,6 +1376,13 @@ magic-string@^0.30.0:
   dependencies:
     "@jridgewell/sourcemap-codec" "^1.4.13"
 
+magic-string@^0.30.10:
+  version "0.30.10"
+  resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.10.tgz#123d9c41a0cb5640c892b041d4cfb3bd0aa4b39e"
+  integrity sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==
+  dependencies:
+    "@jridgewell/sourcemap-codec" "^1.4.15"
+
 make-dir@^2.1.0:
   version "2.1.0"
   resolved "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
@@ -1457,6 +1457,18 @@ mlly@^1.1.1, mlly@^1.2.0:
     pkg-types "^1.0.2"
     ufo "^1.1.1"
 
+moment-timezone@^0.5.45:
+  version "0.5.45"
+  resolved "https://registry.npmmirror.com/moment-timezone/-/moment-timezone-0.5.45.tgz#cb685acd56bac10e69d93c536366eb65aa6bcf5c"
+  integrity sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==
+  dependencies:
+    moment "^2.29.4"
+
+moment@^2.29.4, moment@^2.30.1:
+  version "2.30.1"
+  resolved "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae"
+  integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==
+
 ms@2.1.2:
   version "2.1.2"
   resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
@@ -1476,10 +1488,10 @@ mz@^2.7.0:
     object-assign "^4.0.1"
     thenify-all "^1.0.0"
 
-nanoid@^3.3.6:
-  version "3.3.6"
-  resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
-  integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
+nanoid@^3.3.6, nanoid@^3.3.7:
+  version "3.3.7"
+  resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
+  integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
 
 natural-compare@^1.4.0:
   version "1.4.0"
@@ -1608,9 +1620,9 @@ pathe@^1.1.0:
   integrity sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==
 
 picocolors@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
-  integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
+  integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
 
 picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
   version "2.3.1"
@@ -1693,7 +1705,7 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
   resolved "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
   integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
 
-postcss@^8.0.9, postcss@^8.1.10, postcss@^8.4.21, postcss@^8.4.23:
+postcss@^8.0.9, postcss@^8.4.21, postcss@^8.4.23:
   version "8.4.23"
   resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab"
   integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==
@@ -1702,6 +1714,15 @@ postcss@^8.0.9, postcss@^8.1.10, postcss@^8.4.21, postcss@^8.4.23:
     picocolors "^1.0.0"
     source-map-js "^1.0.2"
 
+postcss@^8.4.38:
+  version "8.4.38"
+  resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e"
+  integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==
+  dependencies:
+    nanoid "^3.3.7"
+    picocolors "^1.0.0"
+    source-map-js "^1.2.0"
+
 prelude-ls@^1.2.1:
   version "1.2.1"
   resolved "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
@@ -1881,21 +1902,16 @@ socket.io-parser@~4.2.4:
     "@socket.io/component-emitter" "~3.1.0"
     debug "~4.3.1"
 
-source-map-js@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
-  integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+source-map-js@^1.0.2, source-map-js@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
+  integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
 
-source-map@^0.6.1, source-map@~0.6.0:
+source-map@~0.6.0:
   version "0.6.1"
   resolved "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
   integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
 
-sourcemap-codec@^1.4.8:
-  version "1.4.8"
-  resolved "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
-  integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
-
 strip-ansi@^6.0.1:
   version "6.0.1"
   resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
@@ -2148,15 +2164,15 @@ vue-router@^4.1.6:
     "@vue/devtools-api" "^6.4.5"
 
 vue@^3.2.47:
-  version "3.2.47"
-  resolved "https://registry.npmmirror.com/vue/-/vue-3.2.47.tgz#3eb736cbc606fc87038dbba6a154707c8a34cff0"
-  integrity sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==
-  dependencies:
-    "@vue/compiler-dom" "3.2.47"
-    "@vue/compiler-sfc" "3.2.47"
-    "@vue/runtime-dom" "3.2.47"
-    "@vue/server-renderer" "3.2.47"
-    "@vue/shared" "3.2.47"
+  version "3.4.31"
+  resolved "https://registry.npmmirror.com/vue/-/vue-3.4.31.tgz#83a3c4dab8302b0e974b0d4b92a2f6a6378ae797"
+  integrity sha512-njqRrOy7W3YLAlVqSKpBebtZpDVg21FPoaq1I7f/+qqBThK9ChAIjkRWgeP6Eat+8C+iia4P3OYqpATP21BCoQ==
+  dependencies:
+    "@vue/compiler-dom" "3.4.31"
+    "@vue/compiler-sfc" "3.4.31"
+    "@vue/runtime-dom" "3.4.31"
+    "@vue/server-renderer" "3.4.31"
+    "@vue/shared" "3.4.31"
 
 webpack-sources@^3.2.3:
   version "3.2.3"