feat: 多端同步服务端与引擎 + Capacitor 安卓工程(v1.13)
This commit is contained in:
@@ -13,6 +13,7 @@ import { downloadCsv } from '../lib/csv'
|
||||
import { hhmm, parseDateStr, todayStr } from '../lib/date'
|
||||
import { computeDay } from '../domain/attendance'
|
||||
import { changeLoginPassword, deleteCurrentUser, getSession } from '../db/auth'
|
||||
import { getSyncLastTime, isSyncConfigured, registerCloudAccount, runSync, type SyncResult } from '../sync/engine'
|
||||
import { showToast } from '../lib/toast'
|
||||
|
||||
const rule = ref<WorkRule>({ work_start: '09:00', work_end: '18:00', grace_minutes: 10, lunch_minutes: 60, remind_weekly: 'friday' })
|
||||
@@ -34,6 +35,54 @@ const newLoginPwd1 = ref('')
|
||||
const newLoginPwd2 = ref('')
|
||||
const accError = ref('')
|
||||
const deleteAccConfirm = ref(false)
|
||||
const syncBase = ref('')
|
||||
const syncUser = ref('')
|
||||
const syncPwd = ref('')
|
||||
const syncStatus = ref('')
|
||||
const syncError = ref('')
|
||||
const syncing = ref(false)
|
||||
const lastSync = ref('')
|
||||
|
||||
async function loadSyncInfo() {
|
||||
syncBase.value = await getSetting('sync_base_url', '')
|
||||
syncUser.value = await getSetting('sync_username', '')
|
||||
syncPwd.value = await getSetting('sync_password', '')
|
||||
lastSync.value = await getSyncLastTime()
|
||||
}
|
||||
|
||||
async function saveSyncConfig() {
|
||||
await setSetting('sync_base_url', syncBase.value.trim())
|
||||
await setSetting('sync_username', syncUser.value.trim())
|
||||
await setSetting('sync_password', syncPwd.value)
|
||||
showToast('同步配置已保存')
|
||||
}
|
||||
|
||||
async function registerCloud() {
|
||||
syncError.value = ''
|
||||
try {
|
||||
await registerCloudAccount(syncBase.value, syncUser.value, syncPwd.value)
|
||||
await saveSyncConfig()
|
||||
showToast('云端账号已注册,可直接同步')
|
||||
} catch (e) {
|
||||
syncError.value = e instanceof Error ? e.message : '注册失败'
|
||||
}
|
||||
}
|
||||
|
||||
async function doSyncNow() {
|
||||
if (syncing.value) return
|
||||
syncError.value = ''
|
||||
syncing.value = true
|
||||
try {
|
||||
await saveSyncConfig()
|
||||
const result: SyncResult = await runSync({ baseUrl: syncBase.value, username: syncUser.value, password: syncPwd.value })
|
||||
lastSync.value = result.at
|
||||
syncStatus.value = '推送 ' + result.pushed + ' 条 · 拉取 ' + result.pulled + ' 条 · 删除 ' + result.removed + ' 条'
|
||||
showToast('同步完成')
|
||||
} catch (e) {
|
||||
syncError.value = e instanceof Error ? e.message : '同步失败'
|
||||
}
|
||||
syncing.value = false
|
||||
}
|
||||
|
||||
async function doChangeLoginPwd() {
|
||||
accError.value = ''
|
||||
@@ -64,6 +113,7 @@ async function load() {
|
||||
aiBase.value = await getSetting('ai_base_url', '')
|
||||
aiKey.value = await getSetting('ai_api_key', '')
|
||||
aiModel.value = await getSetting('ai_model', '')
|
||||
await loadSyncInfo()
|
||||
}
|
||||
|
||||
async function saveAi() {
|
||||
@@ -291,6 +341,31 @@ async function doImport() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card section">
|
||||
<div class="section-title">多端数据同步</div>
|
||||
<div class="muted acc-line">在自建同步服务端注册云端账号后,手机(PWA)、桌面与网页端登录同一账号即可互通数据;密码库以密文同步,主密码永不出本机</div>
|
||||
<div class="ai-grid">
|
||||
<div class="rule-item">
|
||||
<label>同步服务地址</label>
|
||||
<input class="input" v-model="syncBase" placeholder="如 http://124.221.192.176:3780" />
|
||||
</div>
|
||||
<div class="rule-item">
|
||||
<label>云端用户名</label>
|
||||
<input class="input" v-model="syncUser" placeholder="云端账号用户名" />
|
||||
</div>
|
||||
<div class="rule-item">
|
||||
<label>云端密码(至少 6 位)</label>
|
||||
<input type="password" class="input" v-model="syncPwd" placeholder="云端账号密码" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="rule-save">
|
||||
<button class="btn btn-sm" @click="registerCloud">注册云端账号</button>
|
||||
<button class="btn btn-primary btn-sm" :disabled="syncing" @click="doSyncNow">{{ syncing ? '同步中' : '立即同步' }}</button>
|
||||
</div>
|
||||
<div v-if="syncStatus" class="muted acc-line">上次同步:{{ lastSync || '--' }} · {{ syncStatus }}</div>
|
||||
<div v-if="syncError" class="form-error">{{ syncError }}</div>
|
||||
</div>
|
||||
|
||||
<div class="card section">
|
||||
<div class="section-title">AI 润色配置(可选)</div>
|
||||
<div class="ai-grid">
|
||||
|
||||
Reference in New Issue
Block a user