家谱APP首页样式设计完成,落地80%
This commit is contained in:
+218
@@ -0,0 +1,218 @@
|
||||
import { currentUser, genealogies, treeMembers, familyFeeds, familyContent, notifications, joinApplications } from '@/data/mock.js'
|
||||
import { hasRemoteConfig, isMockMode, runtimeConfig } from '@/utils/config.js'
|
||||
import { session } from '@/utils/session.js'
|
||||
|
||||
const successCodes = [0, 200]
|
||||
|
||||
export const unwrapResponse = (response) => {
|
||||
if (!response || typeof response !== 'object' || Array.isArray(response)) return response
|
||||
if (!Object.prototype.hasOwnProperty.call(response, 'code')) return response
|
||||
if (!successCodes.includes(Number(response.code))) {
|
||||
throw new Error(response.msg || '请求未成功')
|
||||
}
|
||||
return Object.prototype.hasOwnProperty.call(response, 'data') ? response.data : response
|
||||
}
|
||||
|
||||
const request = (options, { authenticated = true } = {}) => new Promise((resolve, reject) => {
|
||||
const token = session.getToken()
|
||||
uni.request({
|
||||
...options,
|
||||
url: `${runtimeConfig.baseUrl}${options.url}`,
|
||||
header: {
|
||||
clientid: runtimeConfig.clientId,
|
||||
...(authenticated && token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
...(options.header || {})
|
||||
},
|
||||
success: ({ data, statusCode }) => {
|
||||
if (statusCode < 200 || statusCode >= 300) {
|
||||
reject(new Error(data?.msg || `请求失败(${statusCode})`))
|
||||
return
|
||||
}
|
||||
try {
|
||||
resolve(unwrapResponse(data))
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
},
|
||||
fail: (error) => reject(new Error(error.errMsg || '网络连接失败'))
|
||||
})
|
||||
})
|
||||
|
||||
const authPayload = (payload) => ({
|
||||
clientId: runtimeConfig.clientId,
|
||||
tenantId: runtimeConfig.tenantId,
|
||||
...payload
|
||||
})
|
||||
|
||||
const saveLogin = (loginResult) => {
|
||||
const token = loginResult?.token || loginResult?.accessToken || loginResult?.tokenValue
|
||||
if (!token) throw new Error('登录响应未包含会话令牌')
|
||||
session.saveToken(token)
|
||||
return loginResult
|
||||
}
|
||||
|
||||
const toTreeNode = (person, index = 0) => {
|
||||
const generation = Number(person.generationNo || person.generation || 1)
|
||||
return {
|
||||
...person,
|
||||
id: person.id || person.personId,
|
||||
name: person.personName || person.name || '未命名族人',
|
||||
relation: person.relation || (generation === 1 ? '始祖' : '族人'),
|
||||
generation,
|
||||
years: person.years || [person.birthDate, person.deathDate].filter(Boolean).join('—') || '生卒待补',
|
||||
branch: person.branch || '主支',
|
||||
x: person.x ?? (20 + (index % 4) * 20),
|
||||
y: person.y ?? (generation * 31 - 24)
|
||||
}
|
||||
}
|
||||
|
||||
export const appApi = {
|
||||
async sendSmsCode({ phone, validToken = '' }) {
|
||||
if (isMockMode()) return { success: true }
|
||||
return request({
|
||||
url: '/genealogy/app/auth/sms/code',
|
||||
method: 'POST',
|
||||
data: authPayload({ grantType: 'sms', sceneCode: 'APP_LOGIN', phone, validToken })
|
||||
}, { authenticated: false })
|
||||
},
|
||||
async loginWithPassword({ phone, passwordHash }) {
|
||||
if (isMockMode()) {
|
||||
session.saveToken('mock-session-token')
|
||||
return { token: 'mock-session-token', userId: currentUser.id }
|
||||
}
|
||||
const result = await request({
|
||||
url: '/genealogy/app/auth/login',
|
||||
method: 'POST',
|
||||
data: authPayload({ grantType: 'password', phone, password: passwordHash })
|
||||
}, { authenticated: false })
|
||||
return saveLogin(result)
|
||||
},
|
||||
async loginWithSms({ phone, smsCode }) {
|
||||
if (isMockMode()) {
|
||||
session.saveToken('mock-session-token')
|
||||
return { token: 'mock-session-token', userId: currentUser.id }
|
||||
}
|
||||
const result = await request({
|
||||
url: '/genealogy/app/auth/login/sms',
|
||||
method: 'POST',
|
||||
data: authPayload({ grantType: 'sms', phone, smsCode })
|
||||
}, { authenticated: false })
|
||||
return saveLogin(result)
|
||||
},
|
||||
async getProfile() {
|
||||
return hasRemoteConfig() ? request({ url: '/genealogy/app/auth/profile' }) : currentUser
|
||||
},
|
||||
async getMyGenealogies() {
|
||||
return hasRemoteConfig() ? request({ url: '/genealogy/app/genealogies/mine' }) : genealogies
|
||||
},
|
||||
async createGenealogy(payload) {
|
||||
if (hasRemoteConfig()) return request({ url: '/genealogy/app/genealogies', method: 'POST', data: payload })
|
||||
const created = { ...payload, id: Date.now(), memberCount: 0, activeCount: 0, motto: '敦亲睦族,敬祖传家。', visibility: '仅成员可见' }
|
||||
genealogies.unshift(created)
|
||||
return created
|
||||
},
|
||||
async getGenealogy(genealogyId) {
|
||||
return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}` }) : genealogies.find((item) => String(item.id) === String(genealogyId))
|
||||
},
|
||||
async getOverview(genealogyId) {
|
||||
return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/overview` }) : await this.getGenealogy(genealogyId)
|
||||
},
|
||||
async getTree(genealogyId) {
|
||||
if (hasRemoteConfig()) {
|
||||
const tree = await request({ url: `/genealogy/app/genealogies/${genealogyId}/lineage/tree` })
|
||||
return (tree || []).map(toTreeNode)
|
||||
}
|
||||
return treeMembers.filter((item) => String(item.genealogyId || 1001) === String(genealogyId)).map(toTreeNode)
|
||||
},
|
||||
async getPerson(genealogyId, personId) {
|
||||
if (hasRemoteConfig()) {
|
||||
const person = await request({ url: `/genealogy/app/genealogies/${genealogyId}/lineage/persons/${personId}` })
|
||||
return toTreeNode(person)
|
||||
}
|
||||
const person = treeMembers.find((item) => String(item.id) === String(personId) && String(item.genealogyId || 1001) === String(genealogyId))
|
||||
return person ? toTreeNode(person) : null
|
||||
},
|
||||
async createPerson(genealogyId, payload) {
|
||||
if (hasRemoteConfig()) {
|
||||
const person = await request({ url: `/genealogy/app/genealogies/${genealogyId}/lineage/persons`, method: 'POST', data: payload })
|
||||
return toTreeNode(person)
|
||||
}
|
||||
const person = toTreeNode({
|
||||
...payload,
|
||||
id: Date.now(),
|
||||
genealogyId,
|
||||
relation: Number(payload.generationNo) === 1 ? '始祖' : '族人',
|
||||
x: 50,
|
||||
y: 7
|
||||
})
|
||||
treeMembers.push(person)
|
||||
const genealogy = genealogies.find((item) => String(item.id) === String(genealogyId))
|
||||
if (genealogy) {
|
||||
genealogy.memberCount = (genealogy.memberCount || 0) + 1
|
||||
genealogy.activeCount = (genealogy.activeCount || 0) + 1
|
||||
}
|
||||
return person
|
||||
},
|
||||
async getFeeds(genealogyId) {
|
||||
return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/feeds` }) : familyFeeds
|
||||
},
|
||||
async createFeed(genealogyId, payload) {
|
||||
if (hasRemoteConfig()) return request({ url: `/genealogy/app/genealogies/${genealogyId}/feeds`, method: 'POST', data: payload })
|
||||
const feed = { id: Date.now(), genealogyId, author: currentUser.name, title: '家族动态', content: payload.feedContent, date: '刚刚', type: '族务' }
|
||||
familyFeeds.unshift(feed)
|
||||
return feed
|
||||
},
|
||||
async getArticles(genealogyId) {
|
||||
return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/articles` }) : familyContent.article
|
||||
},
|
||||
async getAlbums(genealogyId) {
|
||||
return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/albums` }) : familyContent.album
|
||||
},
|
||||
async getCeremonies(genealogyId) {
|
||||
return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/ceremonies` }) : familyContent.ceremony
|
||||
},
|
||||
async getGrowthRecords(genealogyId) {
|
||||
return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/growth-records` }) : familyContent.record
|
||||
},
|
||||
async getContent(type, genealogyId) {
|
||||
const loaders = { article: this.getArticles, album: this.getAlbums, ceremony: this.getCeremonies, record: this.getGrowthRecords }
|
||||
if (!loaders[type]) throw new Error('未知内容类型')
|
||||
return loaders[type].call(this, genealogyId)
|
||||
},
|
||||
async getNotifications() {
|
||||
return hasRemoteConfig() ? request({ url: '/genealogy/app/notifications' }) : notifications
|
||||
},
|
||||
async markNotificationRead(notificationId) {
|
||||
if (hasRemoteConfig()) return request({ url: `/genealogy/app/notifications/${notificationId}/read`, method: 'POST' })
|
||||
const notification = notifications.find((item) => String(item.id) === String(notificationId))
|
||||
if (notification) notification.unread = false
|
||||
return { success: true }
|
||||
},
|
||||
async markAllNotificationsRead() {
|
||||
if (hasRemoteConfig()) return request({ url: '/genealogy/app/notifications/read-all', method: 'POST' })
|
||||
notifications.forEach((item) => { item.unread = false })
|
||||
return { success: true }
|
||||
},
|
||||
async getPublicGenealogies() {
|
||||
return hasRemoteConfig() ? request({ url: '/genealogy/app/genealogies/public' }) : genealogies.filter((item) => item.visibility === '公开可申请')
|
||||
},
|
||||
async applyToJoin(genealogyId, payload) {
|
||||
if (hasRemoteConfig()) return request({ url: `/genealogy/app/genealogies/${genealogyId}/join-applies`, method: 'POST', data: payload })
|
||||
joinApplications.unshift({
|
||||
id: Date.now(),
|
||||
genealogyId,
|
||||
name: currentUser.name,
|
||||
phone: currentUser.phone,
|
||||
relation: payload.message || '申请加入家谱',
|
||||
appliedAt: '刚刚',
|
||||
status: 'PENDING'
|
||||
})
|
||||
return { success: true }
|
||||
},
|
||||
async getPendingApplications(genealogyId) {
|
||||
return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/join-applies/pending` }) : joinApplications.filter((item) => String(item.genealogyId || 1001) === String(genealogyId))
|
||||
},
|
||||
async auditApplication(genealogyId, applicationId, approved) {
|
||||
return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/join-applies/${applicationId}/audit`, method: 'PUT', data: { approved } }) : { success: true }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export const runtimeConfig = {
|
||||
mode: 'mock',
|
||||
baseUrl: 'http://182.61.18.23:8080',
|
||||
clientId: '428a8310cd442757ae699df5d894f051',
|
||||
tenantId: '000000'
|
||||
}
|
||||
|
||||
export const isMockMode = () => runtimeConfig.mode === 'mock'
|
||||
export const hasRemoteConfig = () => runtimeConfig.mode === 'remote' && Boolean(runtimeConfig.baseUrl && runtimeConfig.clientId)
|
||||
@@ -0,0 +1,7 @@
|
||||
const CURRENT_GENEALOGY_KEY = 'jiapu_current_genealogy_id'
|
||||
|
||||
export const genealogyContext = {
|
||||
getCurrentGenealogyId: () => uni.getStorageSync(CURRENT_GENEALOGY_KEY) || '',
|
||||
setCurrentGenealogyId: (genealogyId) => uni.setStorageSync(CURRENT_GENEALOGY_KEY, String(genealogyId)),
|
||||
clearCurrentGenealogyId: () => uni.removeStorageSync(CURRENT_GENEALOGY_KEY)
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
const safeAdd = (x, y) => {
|
||||
const lsw = (x & 0xffff) + (y & 0xffff)
|
||||
const msw = (x >> 16) + (y >> 16) + (lsw >> 16)
|
||||
return (msw << 16) | (lsw & 0xffff)
|
||||
}
|
||||
|
||||
const bitRotateLeft = (value, count) => (value << count) | (value >>> (32 - count))
|
||||
const common = (q, a, b, x, s, t) => safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b)
|
||||
const ff = (a, b, c, d, x, s, t) => common((b & c) | ((~b) & d), a, b, x, s, t)
|
||||
const gg = (a, b, c, d, x, s, t) => common((b & d) | (c & (~d)), a, b, x, s, t)
|
||||
const hh = (a, b, c, d, x, s, t) => common(b ^ c ^ d, a, b, x, s, t)
|
||||
const ii = (a, b, c, d, x, s, t) => common(c ^ (b | (~d)), a, b, x, s, t)
|
||||
|
||||
const toWords = (value) => {
|
||||
const words = []
|
||||
for (let index = 0; index < value.length * 8; index += 8) {
|
||||
words[index >> 5] |= (value.charCodeAt(index / 8) & 0xff) << (index % 32)
|
||||
}
|
||||
return words
|
||||
}
|
||||
|
||||
const toHex = (words) => {
|
||||
const alphabet = '0123456789abcdef'
|
||||
let output = ''
|
||||
for (let index = 0; index < words.length * 4; index += 1) {
|
||||
output += alphabet.charAt((words[index >> 2] >> ((index % 4) * 8 + 4)) & 0x0f)
|
||||
output += alphabet.charAt((words[index >> 2] >> ((index % 4) * 8)) & 0x0f)
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
const coreMd5 = (words, bitLength) => {
|
||||
words[bitLength >> 5] |= 0x80 << (bitLength % 32)
|
||||
words[(((bitLength + 64) >>> 9) << 4) + 14] = bitLength
|
||||
|
||||
let a = 1732584193
|
||||
let b = -271733879
|
||||
let c = -1732584194
|
||||
let d = 271733878
|
||||
|
||||
for (let index = 0; index < words.length; index += 16) {
|
||||
const oldA = a
|
||||
const oldB = b
|
||||
const oldC = c
|
||||
const oldD = d
|
||||
|
||||
a = ff(a, b, c, d, words[index], 7, -680876936); d = ff(d, a, b, c, words[index + 1], 12, -389564586); c = ff(c, d, a, b, words[index + 2], 17, 606105819); b = ff(b, c, d, a, words[index + 3], 22, -1044525330)
|
||||
a = ff(a, b, c, d, words[index + 4], 7, -176418897); d = ff(d, a, b, c, words[index + 5], 12, 1200080426); c = ff(c, d, a, b, words[index + 6], 17, -1473231341); b = ff(b, c, d, a, words[index + 7], 22, -45705983)
|
||||
a = ff(a, b, c, d, words[index + 8], 7, 1770035416); d = ff(d, a, b, c, words[index + 9], 12, -1958414417); c = ff(c, d, a, b, words[index + 10], 17, -42063); b = ff(b, c, d, a, words[index + 11], 22, -1990404162)
|
||||
a = ff(a, b, c, d, words[index + 12], 7, 1804603682); d = ff(d, a, b, c, words[index + 13], 12, -40341101); c = ff(c, d, a, b, words[index + 14], 17, -1502002290); b = ff(b, c, d, a, words[index + 15], 22, 1236535329)
|
||||
a = gg(a, b, c, d, words[index + 1], 5, -165796510); d = gg(d, a, b, c, words[index + 6], 9, -1069501632); c = gg(c, d, a, b, words[index + 11], 14, 643717713); b = gg(b, c, d, a, words[index], 20, -373897302)
|
||||
a = gg(a, b, c, d, words[index + 5], 5, -701558691); d = gg(d, a, b, c, words[index + 10], 9, 38016083); c = gg(c, d, a, b, words[index + 15], 14, -660478335); b = gg(b, c, d, a, words[index + 4], 20, -405537848)
|
||||
a = gg(a, b, c, d, words[index + 9], 5, 568446438); d = gg(d, a, b, c, words[index + 14], 9, -1019803690); c = gg(c, d, a, b, words[index + 3], 14, -187363961); b = gg(b, c, d, a, words[index + 8], 20, 1163531501)
|
||||
a = gg(a, b, c, d, words[index + 13], 5, -1444681467); d = gg(d, a, b, c, words[index + 2], 9, -51403784); c = gg(c, d, a, b, words[index + 7], 14, 1735328473); b = gg(b, c, d, a, words[index + 12], 20, -1926607734)
|
||||
a = hh(a, b, c, d, words[index + 5], 4, -378558); d = hh(d, a, b, c, words[index + 8], 11, -2022574463); c = hh(c, d, a, b, words[index + 11], 16, 1839030562); b = hh(b, c, d, a, words[index + 14], 23, -35309556)
|
||||
a = hh(a, b, c, d, words[index + 1], 4, -1530992060); d = hh(d, a, b, c, words[index + 4], 11, 1272893353); c = hh(c, d, a, b, words[index + 7], 16, -155497632); b = hh(b, c, d, a, words[index + 10], 23, -1094730640)
|
||||
a = hh(a, b, c, d, words[index + 13], 4, 681279174); d = hh(d, a, b, c, words[index], 11, -358537222); c = hh(c, d, a, b, words[index + 3], 16, -722521979); b = hh(b, c, d, a, words[index + 6], 23, 76029189)
|
||||
a = hh(a, b, c, d, words[index + 9], 4, -640364487); d = hh(d, a, b, c, words[index + 12], 11, -421815835); c = hh(c, d, a, b, words[index + 15], 16, 530742520); b = hh(b, c, d, a, words[index + 2], 23, -995338651)
|
||||
a = ii(a, b, c, d, words[index], 6, -198630844); d = ii(d, a, b, c, words[index + 7], 10, 1126891415); c = ii(c, d, a, b, words[index + 14], 15, -1416354905); b = ii(b, c, d, a, words[index + 5], 21, -57434055)
|
||||
a = ii(a, b, c, d, words[index + 12], 6, 1700485571); d = ii(d, a, b, c, words[index + 3], 10, -1894986606); c = ii(c, d, a, b, words[index + 10], 15, -1051523); b = ii(b, c, d, a, words[index + 1], 21, -2054922799)
|
||||
a = ii(a, b, c, d, words[index + 8], 6, 1873313359); d = ii(d, a, b, c, words[index + 15], 10, -30611744); c = ii(c, d, a, b, words[index + 6], 15, -1560198380); b = ii(b, c, d, a, words[index + 13], 21, 1309151649)
|
||||
a = ii(a, b, c, d, words[index + 4], 6, -145523070); d = ii(d, a, b, c, words[index + 11], 10, -1120210379); c = ii(c, d, a, b, words[index + 2], 15, 718787259); b = ii(b, c, d, a, words[index + 9], 21, -343485551)
|
||||
|
||||
a = safeAdd(a, oldA); b = safeAdd(b, oldB); c = safeAdd(c, oldC); d = safeAdd(d, oldD)
|
||||
}
|
||||
return [a, b, c, d]
|
||||
}
|
||||
|
||||
export const calcMD5 = (value) => {
|
||||
const utf8Value = unescape(encodeURIComponent(String(value)))
|
||||
return toHex(coreMd5(toWords(utf8Value), utf8Value.length * 8))
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
const TOKEN_KEY = 'jiapu_token'
|
||||
|
||||
export const session = {
|
||||
getToken: () => uni.getStorageSync(TOKEN_KEY) || '',
|
||||
saveToken: (token) => uni.setStorageSync(TOKEN_KEY, token),
|
||||
clear: () => uni.removeStorageSync(TOKEN_KEY)
|
||||
}
|
||||
Reference in New Issue
Block a user