数据备份1/2
This commit is contained in:
parent
4101e83677
commit
9a74b31eac
12
src/App.vue
12
src/App.vue
|
@ -16,6 +16,7 @@ import useLayoutStore from './layout/useLayoutStore'
|
|||
import WelcomePage from './layout/grid/WelcomePage'
|
||||
import TomatoPage from './layout/grid/TomatoPage'
|
||||
import useRouterStore from './useRouterStore'
|
||||
import BackupRecovery from './user/BackupRecovery'
|
||||
const Grid = asyncLoader(() => import('./layout/grid'))
|
||||
const Fox = asyncLoader(() => import('./fox'))
|
||||
const settings = useSettingsStore()
|
||||
|
@ -50,14 +51,21 @@ const layout = useLayoutStore()
|
|||
(layout.state.simple && settings.state.simpleModeShowString.includes('showDock'))
|
||||
"
|
||||
/>
|
||||
<div class="fixed z-40 right-[14%] top-8" v-if="!layout.state.simple ||
|
||||
(layout.state.simple && settings.state.simpleModeShowString.includes('showPet'))">
|
||||
<div
|
||||
class="fixed z-40 right-[14%] top-8"
|
||||
v-if="
|
||||
!layout.state.simple ||
|
||||
(layout.state.simple && settings.state.simpleModeShowString.includes('showPet'))
|
||||
"
|
||||
>
|
||||
<Fox />
|
||||
</div>
|
||||
<DirModal />
|
||||
<GlobalMenu />
|
||||
<WelcomePage></WelcomePage>
|
||||
<TomatoPage></TomatoPage>
|
||||
|
||||
<BackupRecovery v-if="router.path === 'global-backup'"></BackupRecovery>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ export default defineComponent(() => {
|
|||
router.path.startsWith('widget-') ||
|
||||
router.path === 'global-search' ||
|
||||
router.path === 'global-adder' ||
|
||||
router.path === 'global-background'
|
||||
router.path === 'global-background'
|
||||
)
|
||||
const full = ref(false)
|
||||
watch(router, () => {
|
||||
|
@ -93,20 +93,21 @@ export default defineComponent(() => {
|
|||
<AdderPage />
|
||||
) : router.path === 'global-background' ? (
|
||||
<BackgroundSwtich />
|
||||
) : router.path.startsWith('widget-') ? (
|
||||
(() => {
|
||||
const name = router.path.split('-')[1]
|
||||
const selected = widgetList.find((el) => el.name === name)
|
||||
if (!selected)
|
||||
return (
|
||||
<div class="w-full h-full flex justify-center items-center text-black/80">
|
||||
组件维护中
|
||||
</div>
|
||||
)
|
||||
const compo = selected.modal
|
||||
return <compo />
|
||||
})()
|
||||
) : null}
|
||||
) :
|
||||
router.path.startsWith('widget-') ? (
|
||||
(() => {
|
||||
const name = router.path.split('-')[1]
|
||||
const selected = widgetList.find((el) => el.name === name)
|
||||
if (!selected)
|
||||
return (
|
||||
<div class="w-full h-full flex justify-center items-center text-black/80">
|
||||
组件维护中
|
||||
</div>
|
||||
)
|
||||
const compo = selected.modal
|
||||
return <compo />
|
||||
})()
|
||||
) : null}
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -20,7 +20,7 @@ export const apiBase = import.meta.env.PROD
|
|||
|
||||
// 后端 cdn 加速地址
|
||||
export const cdnBase = import.meta.env.PROD ? apiBase : apiBase
|
||||
|
||||
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NzJkYjg5OGUxMjY5NDc1ODYwMmYwMTgifQ.8fpdr_HPgxyU0yr-8f6nGdbHYjsFRlBa2lvjc0Zhe-A
|
||||
// 图片后缀名
|
||||
export const imgArr = [
|
||||
'bmp',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
export type GlobalStr = 'search' | 'block' | 'adder' | 'login' | 'background'
|
||||
export type GlobalStr = 'search' | 'block' | 'adder' | 'login' | 'background' | 'backup'
|
||||
export type SettingStr =
|
||||
| 'user'
|
||||
| 'background'
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import { defineComponent, ref, Transition } from 'vue'
|
||||
import useRouterStore from '@/useRouterStore'
|
||||
import { Radio, RadioGroup } from 'ant-design-vue'
|
||||
import useUserStore from './useUserStore'
|
||||
|
||||
|
||||
export default defineComponent(() => {
|
||||
const router = useRouterStore()
|
||||
const userStore = useUserStore()
|
||||
const select = ref(1)
|
||||
return () => (
|
||||
<div class="fixed left-0 top-0 z-50 w-full">
|
||||
<Transition>
|
||||
<div
|
||||
class="w-full h-screen bg-black/50"
|
||||
|
||||
></div>
|
||||
</Transition>
|
||||
<Transition name="modal">
|
||||
<div class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 overflow-hidden transition-all w-[400px] h-[240px] rounded-lg bg-white/30 ">
|
||||
<div class="w-full h-full flex flex-col justify-between p-4 px-6 rounded-lg bg-white overflow-hidden relative">
|
||||
<div class={"text-center border-b-[#ddd] border-b-[1px] pb-2 "}>选择账号数据</div>
|
||||
|
||||
<span class={"text-[#666] text-[14px]"}>检测到云端数据和本地数据不一致,请选择合并或者覆盖</span>
|
||||
<RadioGroup value={select.value} onChange={(e) => {
|
||||
select.value = e.target.value
|
||||
}} class={"flex flex-col text-[#333] gap-y-3"}>
|
||||
<Radio value={1} class={"text-[#333]"}>合并数据</Radio>
|
||||
<Radio value={2} class={"text-[#333]"}>覆盖数据</Radio>
|
||||
|
||||
</RadioGroup>
|
||||
<div class={"flex justify-end "}
|
||||
onClick={() => {
|
||||
if (select.value === 1) {
|
||||
userStore.comineData()
|
||||
} else {
|
||||
userStore.coverageData()
|
||||
}
|
||||
router.replace('')
|
||||
}}
|
||||
>
|
||||
<button class={"px-10 py-1 hover:opacity-80 duration-150 bg-[#4a7aff] text-white rounded-lg"}>确定</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
)
|
||||
})
|
|
@ -39,12 +39,13 @@ export default defineComponent(() => {
|
|||
</div>
|
||||
<div class="flex py-2">
|
||||
<div class={labelStyle}>性别:</div>
|
||||
<Tag color={user.profile.gender === 1 ? 'blue' : 'red'}>
|
||||
{user.profile.gender === 1 ? '男' : '女'}
|
||||
<Tag color={user.profile.gender === 1 ? 'blue' : user.profile.gender === 2 ? 'red' : 'gray'}>
|
||||
{user.profile.gender === 1 ? '男' : user.profile.gender === 2 ? '女' : '未知'}
|
||||
</Tag>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)
|
||||
}
|
||||
|
||||
<div class="flex justify-around items-center my-10">
|
||||
{user.isLogin ? (
|
||||
|
@ -90,6 +91,6 @@ export default defineComponent(() => {
|
|||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
)
|
||||
})
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import type { Block, Layout } from '@/layout/layout.types'
|
||||
import useLayoutStore from '@/layout/useLayoutStore'
|
||||
import useRouterStore from '@/useRouterStore'
|
||||
import request from '@/utils/request'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
import { computed, reactive, ref, toRefs, watch } from 'vue'
|
||||
|
||||
interface UserInfo {
|
||||
id: string
|
||||
|
@ -41,21 +42,55 @@ function getLinkList(data: Layout) {
|
|||
}
|
||||
return list
|
||||
}
|
||||
function areArraysEqualById(arr1: Block[], arr2: Block[]): boolean {
|
||||
if (arr1.length !== arr2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 将 arr2 转换为一个以 id 为键的映射
|
||||
const arr2Map = new Map(arr2.map(item => [item.id, item]));
|
||||
|
||||
// 检查 arr1 中的每个 item 是否在 arr2 中存在,并且值是否相同
|
||||
for (const item1 of arr1) {
|
||||
const item2 = arr2Map.get(item1.id);
|
||||
if (!item2 || JSON.stringify(item1) !== JSON.stringify(item2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export default defineStore('user', () => {
|
||||
const token = ref(localStorage.getItem('token') || '')
|
||||
watch(token, (val) => {
|
||||
localStorage.setItem('token', val)
|
||||
})
|
||||
const remoteData = ref<Layout | null>(null)
|
||||
const remoteAddList = ref<Block[]>([])
|
||||
|
||||
const profile = reactive({ ...defaultUserInfo })
|
||||
const layout = useLayoutStore()
|
||||
|
||||
const isLogin = computed(() => !!token.value && !!profile.id)
|
||||
|
||||
watch(
|
||||
token,
|
||||
async (val) => {
|
||||
localStorage.setItem('token', val)
|
||||
|
||||
if (!val) return
|
||||
const res = await request<UserInfo>('GET', '/api/profile')
|
||||
Object.assign(profile, res)
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
token,
|
||||
async (val) => {
|
||||
if (!val) return
|
||||
const res = await request<UserInfo>('GET', '/api/profile')
|
||||
Object.assign(profile, res)
|
||||
console.log(val);
|
||||
|
||||
const data = await request<Layout>('GET', '/api/backup')
|
||||
if (!data) return
|
||||
const remoteList = getLinkList(data)
|
||||
const localList = getLinkList(layout.state)
|
||||
const addList: Block[] = []
|
||||
|
@ -64,21 +99,37 @@ export default defineStore('user', () => {
|
|||
addList.push(item)
|
||||
}
|
||||
}
|
||||
if (addList.length > 0) {
|
||||
if (addList.length > 0 || areArraysEqualById(remoteList, localList) || (remoteList.length !== localList.length)) {
|
||||
// TODO: 交给张阳
|
||||
remoteAddList.value = addList
|
||||
remoteData.value = data
|
||||
useRouterStore().go('global-backup')
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
const isLogin = computed(() => !!token.value && !!profile.id)
|
||||
const logout = () => {
|
||||
token.value = ''
|
||||
Object.assign(profile, { ...defaultUserInfo })
|
||||
}
|
||||
const comineData = () => {
|
||||
if (!remoteData.value) return
|
||||
Object.assign(layout.state.content[layout.state.currentPage].pages, remoteAddList.value)
|
||||
|
||||
}
|
||||
const coverageData = () => {
|
||||
if (!remoteData.value) return
|
||||
console.log({ ...layout.state });
|
||||
console.log({ ...remoteData.value });
|
||||
|
||||
Object.assign(layout.state, remoteData.value)
|
||||
|
||||
}
|
||||
return {
|
||||
token,
|
||||
profile,
|
||||
isLogin,
|
||||
logout
|
||||
logout,
|
||||
coverageData,
|
||||
comineData
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue