diff --git a/sql/update_20260224_spider_news.sql b/sql/update_20260224_spider_news.sql index 1a43a3e..fa1f68f 100644 --- a/sql/update_20260224_spider_news.sql +++ b/sql/update_20260224_spider_news.sql @@ -2,6 +2,7 @@ CREATE TABLE IF NOT EXISTS `pap_spider_news` ( `id` int(11) NOT NULL AUTO_INCREMENT, `origin_id` int(11) NOT NULL DEFAULT 0 COMMENT '原站文章ID', + `type` varchar(20) NOT NULL DEFAULT 'news' COMMENT '来源类型 news/about', `title` varchar(500) NOT NULL DEFAULT '' COMMENT '文章标题', `category` varchar(100) DEFAULT '' COMMENT '原站栏目分类', `publish_time` datetime DEFAULT NULL COMMENT '原站发布时间', @@ -16,6 +17,6 @@ CREATE TABLE IF NOT EXISTS `pap_spider_news` ( `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), - UNIQUE KEY `uk_origin_id` (`origin_id`), + UNIQUE KEY `uk_type_origin_id` (`type`, `origin_id`), KEY `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='爬虫-新闻数据'; diff --git a/sql/update_20260225_spider_to_article.sql b/sql/update_20260225_spider_to_article.sql new file mode 100644 index 0000000..ba80900 --- /dev/null +++ b/sql/update_20260225_spider_to_article.sql @@ -0,0 +1,23 @@ +-- 将 pap_spider_news 中 id=119~126 的公益项目数据转入 pap_article,column_id=5 +-- content 优先取已转存的 content,其次取原始 content_original +INSERT INTO `pap_article` (`column_id`, `title`, `summary`, `content`, `cover`, `category`, `is_top`, `status`, `publish_time`, `sort`, `create_time`) +SELECT + 5 AS column_id, + s.title, + '' AS summary, + COALESCE(s.content, s.content_original, '') AS content, + COALESCE(s.cover, s.cover_original, '') AS cover, + s.category, + 0 AS is_top, + 1 AS status, + s.publish_time, + 0 AS sort, + NOW() +FROM `pap_spider_news` s +WHERE s.id IN (119, 120, 121, 122, 123, 124, 125, 126); + +-- 回写 article_id 到 spider_news(方便追溯) +UPDATE `pap_spider_news` sn +INNER JOIN `pap_article` a ON a.title = sn.title AND a.column_id = 5 +SET sn.article_id = a.id, sn.status = 3 +WHERE sn.id IN (119, 120, 121, 122, 123, 124, 125, 126); diff --git a/sql/update_20260225_spider_type.sql b/sql/update_20260225_spider_type.sql new file mode 100644 index 0000000..b41fad5 --- /dev/null +++ b/sql/update_20260225_spider_type.sql @@ -0,0 +1,4 @@ +-- 给spider_news表添加type字段,区分news和about +ALTER TABLE `pap_spider_news` ADD COLUMN `type` varchar(20) NOT NULL DEFAULT 'news' COMMENT '来源类型 news/about' AFTER `origin_id`; +ALTER TABLE `pap_spider_news` DROP INDEX `uk_origin_id`; +ALTER TABLE `pap_spider_news` ADD UNIQUE KEY `uk_type_origin_id` (`type`, `origin_id`); diff --git a/sql/update_20260227_column_extend.sql b/sql/update_20260227_column_extend.sql new file mode 100644 index 0000000..3d8760b --- /dev/null +++ b/sql/update_20260227_column_extend.sql @@ -0,0 +1,12 @@ +-- 栏目表扩展:增加前台展示字段 +ALTER TABLE `pap_column` ADD COLUMN `name_en` varchar(200) DEFAULT '' COMMENT '英文名称' AFTER `name`; +ALTER TABLE `pap_column` ADD COLUMN `description` varchar(500) DEFAULT '' COMMENT '栏目描述/副标题' AFTER `name_en`; +ALTER TABLE `pap_column` ADD COLUMN `banner_image` varchar(500) DEFAULT '' COMMENT '栏目Banner背景图' AFTER `description`; + +-- 更新现有一级栏目数据 +UPDATE `pap_column` SET name_en = 'ABOUT US', description = '苍穹 笃行 创新 守止' WHERE `key` = 'about'; +UPDATE `pap_column` SET name_en = 'PUBLIC WELFARE PROJECTS', description = '聚焦健康公益,守护每一份希望' WHERE `key` = 'project'; +UPDATE `pap_column` SET name_en = 'PARTY BUILDING', description = '不忘初心 牢记使命' WHERE `key` = 'party'; +UPDATE `pap_column` SET name_en = 'INFORMATION DISCLOSURE', description = '公开透明 接受监督' WHERE `key` = 'disclosure'; +UPDATE `pap_column` SET name_en = 'NEWS CENTER', description = '了解基金会最新动态' WHERE `key` = 'news'; +UPDATE `pap_column` SET name_en = 'CONTACT US', description = '期待与您携手同行' WHERE `key` = 'contact'; diff --git a/src/config/adapter.js b/src/config/adapter.js index 7ddfafd..95fe283 100644 --- a/src/config/adapter.js +++ b/src/config/adapter.js @@ -44,7 +44,8 @@ exports.model = { port: '26821', user: 'root', password: '5orKUdDN3QhESVcS', - dateStrings: true + dateStrings: true, + connectionLimit: 10 } }; diff --git a/src/config/router.js b/src/config/router.js index 6c28447..30bd2b0 100644 --- a/src/config/router.js +++ b/src/config/router.js @@ -3,6 +3,10 @@ module.exports = [ ['/', 'index/index'], ['/index', 'index/index'], + // 一级栏目页面(带子栏目) + ['/column/:key', 'column/index', 'get'], + ['/column/:key/:child', 'column/index', 'get'], + // 后台管理路由 ['/admin/login', 'admin/auth/login', 'get'], ['/admin/auth/login', 'admin/auth/doLogin', 'post'], @@ -110,6 +114,7 @@ module.exports = [ // 爬虫工具 ['/admin/spider/fetch-list', 'admin/spider/fetchList'], + ['/admin/spider/fetch-about-list', 'admin/spider/fetchAboutList'], ['/admin/spider/fetch-detail', 'admin/spider/fetchDetail'], ['/admin/spider/transfer-images', 'admin/spider/transferImages'], ['/admin/spider/sync', 'admin/spider/sync'], diff --git a/src/controller/admin/dashboard.js b/src/controller/admin/dashboard.js index 18d44d1..123c235 100644 --- a/src/controller/admin/dashboard.js +++ b/src/controller/admin/dashboard.js @@ -20,18 +20,9 @@ module.exports = class extends Base { // 待办事项(后续从数据库获取) this.assign('todoList', []); - // 动态栏目菜单(后续从接口获取) - this.assign('columnMenus', await this.getColumnMenus()); - // 当前登录用户 this.assign('adminUser', this.adminUser || {}); return this.display(); } - - // 获取动态栏目菜单(暂时返回空,后续从数据库获取) - async getColumnMenus() { - // TODO: 从数据库获取栏目配置 - return []; - } }; diff --git a/src/controller/admin/spider.js b/src/controller/admin/spider.js index b1426b9..ac47768 100644 --- a/src/controller/admin/spider.js +++ b/src/controller/admin/spider.js @@ -26,7 +26,7 @@ module.exports = class extends Base { async fetchListAction() { try { const res = await axios.post( - `${ORIGIN.listUrl}?act=contentlist&id=&type=news`, + `${ORIGIN.listUrl}?act=contentlist&id=&type=product`, 'order=asc&limit=9999&offset=0', { headers: { @@ -53,7 +53,7 @@ module.exports = class extends Base { const publishTime = this.extractTime(row.c_addtime); // 检查是否已存在 - const exists = await model.where({ origin_id: originId }).find(); + const exists = await model.where({ origin_id: originId, type: 'news' }).find(); if (exists && exists.id) { skipped++; continue; @@ -64,6 +64,7 @@ module.exports = class extends Base { title, category, publish_time: publishTime, + type: 'product', status: 0 }); inserted++; @@ -80,6 +81,67 @@ module.exports = class extends Base { } } + /** + * 步骤1b:拉取about列表并入库 + * GET /admin/spider/fetch-about-list + * 参数: sid (可选,指定栏目sid) + */ + async fetchAboutListAction() { + try { + const sid = this.get('sid') || ''; + const res = await axios.post( + `${ORIGIN.listUrl}?act=aboutlist&sid=${sid}`, + 'order=asc&limit=9999&offset=0', + { + headers: { + Cookie: ORIGIN.cookie, + 'Content-Type': 'application/x-www-form-urlencoded' + } + } + ); + + const rows = res.data; + if (!rows || !Array.isArray(rows)) { + return this.json({ code: 1, msg: 'about列表数据获取失败' }); + } + + const model = this.model('spider_news'); + let inserted = 0; + let skipped = 0; + + for (const row of rows) { + const originId = parseInt(row.id); + const title = this.extractTitle(row.a_name); + const category = row.sid || ''; + + // 检查是否已存在 + const exists = await model.where({ origin_id: originId, type: 'about' }).find(); + if (exists && exists.id) { + skipped++; + continue; + } + + await model.add({ + origin_id: originId, + title, + category, + type: 'about', + status: 0 + }); + inserted++; + } + + return this.json({ + code: 0, + msg: `about列表拉取完成,新增${inserted}条,跳过${skipped}条`, + data: { total: rows.length, inserted, skipped } + }); + } catch (err) { + think.logger.error('拉取about列表失败:', err); + return this.json({ code: 1, msg: '拉取about列表失败: ' + err.message }); + } + } + /** * 步骤2:爬取详情页内容 * GET /admin/spider/fetch-detail @@ -87,14 +149,19 @@ module.exports = class extends Base { */ async fetchDetailAction() { const targetId = this.get('id'); + const type = this.get('type') || ''; const model = this.model('spider_news'); - let list; + let where = { status: 0 }; if (targetId) { - list = await model.where({ origin_id: targetId }).select(); - } else { - list = await model.where({ status: 0 }).select(); + where = { origin_id: targetId }; + if (type) where.type = type; } + if (type && !targetId) { + where.type = type; + } + + const list = await model.where(where).select(); if (!list.length) { return this.json({ code: 0, msg: '没有待爬取的记录' }); @@ -105,7 +172,7 @@ module.exports = class extends Base { for (const item of list) { try { - const detailUrl = `${ORIGIN.base}/?news/${item.origin_id}.html`; + const detailUrl = `${ORIGIN.base}/?${type}/${item.origin_id}.html` const res = await axios.get(detailUrl, { headers: { Cookie: ORIGIN.cookie }, responseType: 'arraybuffer' @@ -168,15 +235,20 @@ module.exports = class extends Base { */ async transferImagesAction() { const targetId = this.get('id'); + const type = this.get('type') || ''; const model = this.model('spider_news'); - let list; + let where = { status: 1 }; if (targetId) { - list = await model.where({ origin_id: targetId }).select(); - } else { - list = await model.where({ status: 1 }).select(); + where = { origin_id: targetId }; + if (type) where.type = type; + } + if (type && !targetId) { + where.type = type; } + const list = await model.where(where).select(); + if (!list.length) { return this.json({ code: 0, msg: '没有待转存图片的记录' }); } diff --git a/src/controller/admin/system/column.js b/src/controller/admin/system/column.js index f20da62..124ec01 100644 --- a/src/controller/admin/system/column.js +++ b/src/controller/admin/system/column.js @@ -54,6 +54,9 @@ module.exports = class extends Base { const id = await model.add({ parent_id: data.parent_id || 0, name: data.name, + name_en: data.name_en || '', + description: data.description || '', + banner_image: data.banner_image || '', key: data.key || '', icon: data.icon || '', type: data.type || '', @@ -88,6 +91,9 @@ module.exports = class extends Base { await this.model('column').where({ id }).update({ parent_id: data.parent_id || 0, name: data.name, + name_en: data.name_en || '', + description: data.description || '', + banner_image: data.banner_image || '', key: data.key || '', icon: data.icon || '', type: data.type || '', diff --git a/src/controller/base.js b/src/controller/base.js index 7a2d9ef..9254aff 100644 --- a/src/controller/base.js +++ b/src/controller/base.js @@ -12,6 +12,7 @@ const WHITE_LIST = [ '/admin/logout', // 爬虫工具 '/admin/spider/fetch-list', + '/admin/spider/fetch-about-list', '/admin/spider/fetch-detail', '/admin/spider/transfer-images', '/admin/spider/sync', @@ -49,8 +50,9 @@ module.exports = class extends think.Controller { } } - // 白名单放行 - if (WHITE_LIST.includes(path)) { + // 白名单放行(同时匹配带.html后缀的路径) + const pathNoSuffix = path.replace(/\.html$/, ''); + if (WHITE_LIST.includes(path) || WHITE_LIST.includes(pathNoSuffix)) { return; } diff --git a/src/controller/column.js b/src/controller/column.js new file mode 100644 index 0000000..5110194 --- /dev/null +++ b/src/controller/column.js @@ -0,0 +1,112 @@ +const Base = require('./base.js'); + +module.exports = class extends Base { + async indexAction() { + const columnKey = this.get('key'); + const childKey = this.get('child'); + + if (!columnKey) return this.redirect('/'); + + // 获取所有栏目 + const columns = await this.model('column').where({ is_deleted: 0, visible: 1 }).order('sort ASC').select(); + const navMenus = this.buildNavTree(columns); + + // 找到当前一级栏目 + const column = columns.find(c => c.key === columnKey && c.parent_id === 0); + if (!column) return this.redirect('/'); + + // 获取子栏目 + const children = columns.filter(c => c.parent_id === column.id).sort((a, b) => a.sort - b.sort); + + // 确定当前选中的子栏目 + let currentChild = null; + if (childKey) { + currentChild = children.find(c => c.key === childKey); + } + if (!currentChild && children.length > 0) { + currentChild = children[0]; + } + + // 根据子栏目类型加载内容 + let contentData = {}; + if (currentChild) { + contentData = await this.loadContent(currentChild); + } + + // 网站配置 + const configList = await this.model('config').select(); + const siteConfig = {}; + configList.forEach(c => { siteConfig[c.key] = c.value; }); + + this.assign({ + navMenus, + column, + children, + currentChild, + contentData, + siteConfig, + currentColumnKey: columnKey + }); + + return this.display('column_index'); + } + + // 根据类型加载内容 + async loadContent(child) { + const colId = child.id; + const type = child.type; + + switch (type) { + case 'page': { + const page = await this.model('page').where({ column_id: colId, is_deleted: 0 }).find(); + return { type: 'page', page: page || {} }; + } + case 'article': { + const list = await this.model('article').where({ column_id: colId, status: 1, is_deleted: 0 }) + .order('is_top DESC, sort DESC, publish_time DESC').limit(20).select(); + // 格式化日期 + list.forEach(item => { + if (item.publish_time) { + const d = new Date(item.publish_time); + item.publish_date = d.getFullYear() + '.' + String(d.getMonth() + 1).padStart(2, '0') + '.' + String(d.getDate()).padStart(2, '0'); + } + }); + return { type: 'article', list }; + } + case 'image': { + const list = await this.model('image').where({ column_id: colId, status: 1, is_deleted: 0 }) + .order('sort ASC').select(); + return { type: 'image', list }; + } + case 'text': { + const list = await this.model('text').where({ column_id: colId, status: 1, is_deleted: 0 }) + .order('sort ASC, id DESC').select(); + return { type: 'text', list }; + } + case 'person': { + const list = await this.model('person').where({ column_id: colId, status: 1, is_deleted: 0 }) + .order('sort ASC').select(); + return { type: 'person', list }; + } + case 'job': { + const list = await this.model('job').where({ column_id: colId, status: 1, is_deleted: 0 }) + .order('sort ASC, id DESC').select(); + return { type: 'job', list }; + } + default: + return { type: type || 'empty' }; + } + } + + // 构建导航树 + buildNavTree(columns) { + const tree = []; + const map = {}; + columns.forEach(item => { map[item.id] = { ...item, children: [] }; }); + columns.forEach(item => { + if (item.parent_id === 0) tree.push(map[item.id]); + else if (map[item.parent_id]) map[item.parent_id].children.push(map[item.id]); + }); + return tree; + } +}; diff --git a/src/controller/index.js b/src/controller/index.js index 6447f83..3cf57cb 100644 --- a/src/controller/index.js +++ b/src/controller/index.js @@ -1,10 +1,26 @@ const Base = require('./base.js'); +// 首页数据内存缓存 +let homeCache = null; +let homeCacheTime = 0; +const CACHE_TTL = 60 * 1000; // 60秒缓存 + module.exports = class extends Base { async indexAction() { + const t0 = Date.now(); + + // 检查缓存 + if (homeCache && (Date.now() - homeCacheTime < CACHE_TTL)) { + console.log(`[perf] cache hit, age: ${Date.now() - homeCacheTime}ms`); + this.assign(homeCache); + return this.display(); + } + // 获取栏目配置 const columns = await this.model('column').where({ is_deleted: 0, visible: 1 }).order('sort ASC').select(); - + const t1 = Date.now(); + console.log(`[perf] columns: ${t1 - t0}ms`); + // 构建导航菜单树 const navMenus = this.buildNavTree(columns); @@ -14,71 +30,76 @@ module.exports = class extends Base { const colMap = {}; homeChildren.forEach(c => { colMap[c.key] = c.id; }); - // 获取Banner数据(根据栏目key查找column_id,或者column_id=0表示通用) const bannerColId = colMap['home-banner'] || 0; - const banners = await this.model('banner').where({ - status: 1, - is_deleted: 0 - }).where('column_id = ' + bannerColId + ' OR column_id = 0').order('sort ASC').select(); - - // 获取捐赠统计 const dataColId = colMap['home-data'] || 0; - let donationStat = await this.model('donation_stat').where({ - column_id: dataColId - }).find(); + const projectColId = colMap['home-project'] || 0; + const newsColId = colMap['home-news'] || 0; + const partnerColId = colMap['home-partner'] || 0; + + const t2 = Date.now(); + // 并行查询所有数据 + const [ + banners, + donationStatResult, + donationIncome, + donationExpense, + medicines, + projects, + newsList, + partners, + configList + ] = await Promise.all([ + this.model('banner').where({ status: 1, is_deleted: 0 }) + .where('column_id = ' + bannerColId + ' OR column_id = 0').order('sort ASC').select(), + this.model('donation_stat').where({ column_id: dataColId }).find(), + this.model('donation').where({ type: 'income', status: 1, is_deleted: 0 }) + .where('column_id = ' + dataColId + ' OR column_id = 0').order('record_date DESC').limit(10).select(), + this.model('donation').where({ type: 'expense', status: 1, is_deleted: 0 }) + .where('column_id = ' + dataColId + ' OR column_id = 0').order('record_date DESC').limit(10).select(), + this.model('medicine').where({ status: 1, is_deleted: 0 }) + .order('distribute_date DESC').limit(20).select(), + this.model('article').where({ status: 1, is_deleted: 0 }) + .where('column_id = ' + projectColId + ' OR column_id = 0').order('sort DESC, publish_time DESC').limit(5).select(), + this.model('article').where({ status: 1, is_deleted: 0 }) + .where('column_id = ' + newsColId + ' OR column_id = 0').order('is_top DESC, publish_time DESC').limit(5).select(), + this.model('image').where({ status: 1, is_deleted: 0 }) + .where('column_id = ' + partnerColId + ' OR column_id = 0').order('sort ASC').select(), + this.model('config').select() + ]); + const t3 = Date.now(); + console.log(`[perf] parallel queries: ${t3 - t2}ms`); + + // 捐赠统计兜底 + let donationStat = donationStatResult; if (!donationStat || !donationStat.id) { donationStat = await this.model('donation_stat').where({ column_id: 0 }).find(); } donationStat = donationStat || { total_income: 0, total_expense: 0, year_income: 0 }; - - // 获取捐赠明细(收入/支出各取最新10条) - const donationIncome = await this.model('donation').where({ - type: 'income', - status: 1, - is_deleted: 0 - }).where('column_id = ' + dataColId + ' OR column_id = 0').order('record_date DESC').limit(10).select(); - - const donationExpense = await this.model('donation').where({ - type: 'expense', - status: 1, - is_deleted: 0 - }).where('column_id = ' + dataColId + ' OR column_id = 0').order('record_date DESC').limit(10).select(); - - // 获取药品援助数据(只显示已发放的) - const medicines = await this.model('medicine').where({ - status: 1, - is_deleted: 0 - }).order('distribute_date DESC').limit(20).select(); - - // 获取公益项目 - const projectColId = colMap['home-project'] || 0; - const projects = await this.model('article').where({ - status: 1, - is_deleted: 0 - }).where('column_id = ' + projectColId + ' OR column_id = 0').order('sort DESC, publish_time DESC').limit(5).select(); - - // 获取新闻动态 - const newsColId = colMap['home-news'] || 0; - const newsList = await this.model('article').where({ - status: 1, - is_deleted: 0 - }).where('column_id = ' + newsColId + ' OR column_id = 0').order('is_top DESC, publish_time DESC').limit(5).select(); - - // 获取合作伙伴 - const partnerColId = colMap['home-partner'] || 0; - const partners = await this.model('image').where({ - status: 1, - is_deleted: 0 - }).where('column_id = ' + partnerColId + ' OR column_id = 0').order('sort ASC').select(); - - // 获取网站配置 - const configList = await this.model('config').select(); + + // 格式化日期 + const formatDate = (str) => { + if (!str) return { date: '', day: '', ym: '' }; + const d = new Date(str); + if (isNaN(d.getTime())) return { date: '', day: '', ym: '' }; + const y = d.getFullYear(); + const m = String(d.getMonth() + 1).padStart(2, '0'); + const dd = String(d.getDate()).padStart(2, '0'); + return { date: y + '.' + m + '.' + dd, day: dd, ym: y + '.' + m }; + }; + newsList.forEach(item => { + const fd = formatDate(item.publish_time); + item.publish_date = fd.date; + item.publish_day = fd.day; + item.publish_ym = fd.ym; + }); + + // 网站配置 const siteConfig = {}; configList.forEach(c => { siteConfig[c.key] = c.value; }); - this.assign({ + const data = { navMenus, banners, donationStat, @@ -90,8 +111,16 @@ module.exports = class extends Base { partners, siteConfig, now: new Date() - }); + }; + + // 写入缓存 + homeCache = data; + homeCacheTime = Date.now(); + + this.assign(data); + const t4 = Date.now(); + console.log(`[perf] TOTAL: ${t4 - t0}ms`); return this.display(); } diff --git a/view/admin/auth_login.html b/view/admin/auth_login.html index e7f2d6b..a048824 100644 --- a/view/admin/auth_login.html +++ b/view/admin/auth_login.html @@ -199,10 +199,10 @@ document.getElementById('loginForm').addEventListener('submit', async function(e body: JSON.stringify({ username, password, remember }) }); const data = await res.json(); - if (data.errno === 0) { + if (data.code === 0) { window.location.href = '/admin/dashboard.html'; } else { - errorMsg.textContent = data.errmsg || '登录失败'; + errorMsg.textContent = data.msg || '登录失败'; errorMsg.classList.add('show'); } } catch (err) { diff --git a/view/admin/system/column_index.html b/view/admin/system/column_index.html index 62b6adc..1a74890 100644 --- a/view/admin/system/column_index.html +++ b/view/admin/system/column_index.html @@ -46,6 +46,19 @@ + + + + + + + +
+ + 上传图片 + 清除 +
+
@@ -112,6 +125,19 @@ + + + + + + + +
+ + 上传图片 + 清除 +
+
@@ -288,7 +314,7 @@ const app = createApp({ const saving = ref(false); const parentIsSinglePage = ref(false); const form = reactive({ - id: null, parent_id: 0, name: '', key: '', icon: '', type: '', is_single_page: 0, sort: 1, visible: 1, + id: null, parent_id: 0, name: '', name_en: '', description: '', banner_image: '', key: '', icon: '', type: '', is_single_page: 0, sort: 1, visible: 1, link: '', seo_title: '', seo_keywords: '', seo_description: '', slug: '' }); @@ -304,6 +330,25 @@ const app = createApp({ iconPickerVisible.value = false; } + // Banner图片上传 + function uploadBanner() { + const input = document.createElement('input'); + input.type = 'file'; + input.accept = 'image/*'; + input.onchange = async (e) => { + const file = e.target.files[0]; + if (!file) return; + const fd = new FormData(); + fd.append('file', file); + try { + const res = await fetch('/admin/upload', { method: 'POST', body: fd }).then(r => r.json()); + if (res.code === 0) { form.banner_image = res.data.url; } + else { ElementPlus.ElMessage.error(res.msg || '上传失败'); } + } catch { ElementPlus.ElMessage.error('上传失败'); } + }; + input.click(); + } + const showSeoTab = computed(() => { if (form.parent_id === 0 && form.is_single_page === 1) return true; if (form.parent_id !== 0 && !parentIsSinglePage.value) return true; @@ -338,7 +383,7 @@ const app = createApp({ activeTab.value = 'basic'; parentIsSinglePage.value = parentData ? !!parentData.is_single_page : false; Object.assign(form, { - id: null, parent_id: parentId || 0, name: '', key: '', icon: '', type: '', is_single_page: 0, sort: 1, visible: 1, + id: null, parent_id: parentId || 0, name: '', name_en: '', description: '', banner_image: '', key: '', icon: '', type: '', is_single_page: 0, sort: 1, visible: 1, link: '', seo_title: '', seo_keywords: '', seo_description: '', slug: '' }); dialogVisible.value = true; @@ -354,7 +399,8 @@ const app = createApp({ parentIsSinglePage.value = false; } Object.assign(form, { - id: row.id, parent_id: row.parent_id || 0, name: row.name, key: row.key || '', icon: row.icon || '', + id: row.id, parent_id: row.parent_id || 0, name: row.name, name_en: row.name_en || '', description: row.description || '', + banner_image: row.banner_image || '', key: row.key || '', icon: row.icon || '', type: row.type || '', is_single_page: row.is_single_page || 0, sort: row.sort || 1, visible: row.visible, link: row.link || '', seo_title: row.seo_title || '', seo_keywords: row.seo_keywords || '', seo_description: row.seo_description || '', slug: row.slug || '' @@ -412,7 +458,8 @@ const app = createApp({ iconPickerVisible, iconSearch, filteredIcons, selectIcon, formDrawerVisible, formDrawerTitle, formFields, formSaving, loadList, toggleAllTree, showAddModal, editItem, saveItem, deleteItem, - openFormConfig, addFormField, moveField, removeField, saveFormConfig + openFormConfig, addFormField, moveField, removeField, saveFormConfig, + uploadBanner }; } }); diff --git a/view/column/_article.html b/view/column/_article.html new file mode 100644 index 0000000..05b4299 --- /dev/null +++ b/view/column/_article.html @@ -0,0 +1,26 @@ + +
+ {% if contentData.list and contentData.list.length > 0 %} + {% for item in contentData.list %} + + {% if item.cover %} +
+ {{item.title}} +
+ {% endif %} + +
+ {% endfor %} + {% else %} +
暂无内容
+ {% endif %} +
diff --git a/view/column/_image.html b/view/column/_image.html new file mode 100644 index 0000000..00b21e6 --- /dev/null +++ b/view/column/_image.html @@ -0,0 +1,19 @@ + +
+ {% if contentData.list and contentData.list.length > 0 %} + {% for item in contentData.list %} +
+
+ {% if item.link %} + {{item.title}} + {% else %} + {{item.title}} + {% endif %} +
+ {% if item.title %}
{{item.title}}
{% endif %} +
+ {% endfor %} + {% else %} +
暂无内容
+ {% endif %} +
diff --git a/view/column/_job.html b/view/column/_job.html new file mode 100644 index 0000000..4ad4228 --- /dev/null +++ b/view/column/_job.html @@ -0,0 +1,16 @@ + +
+ {% if contentData.list and contentData.list.length > 0 %} + {% for item in contentData.list %} +
+
+

{{item.title}}

+ {% if item.location %}{{item.location}}{% endif %} +
+ {% if item.description %}

{{item.description}}

{% endif %} +
+ {% endfor %} + {% else %} +
暂无招聘信息
+ {% endif %} +
diff --git a/view/column/_page.html b/view/column/_page.html new file mode 100644 index 0000000..412146f --- /dev/null +++ b/view/column/_page.html @@ -0,0 +1,8 @@ + +
+ {% if contentData.page and contentData.page.content %} +
{{contentData.page.content | safe}}
+ {% else %} +
暂无内容
+ {% endif %} +
diff --git a/view/column/_person.html b/view/column/_person.html new file mode 100644 index 0000000..a0d6865 --- /dev/null +++ b/view/column/_person.html @@ -0,0 +1,24 @@ + +
+ {% if contentData.list and contentData.list.length > 0 %} + {% for item in contentData.list %} +
+
+ {% if item.avatar %} + {{item.name}} + {% else %} +
{{item.name[0] | default('?')}}
+ {% endif %} +
+
+
{{item.name}}
+ {% if item.title %}
{{item.title}}
{% endif %} + {% if item.description %}
{{item.description}}
{% endif %} + {% if item.category %}
{{item.category}}
{% endif %} +
+
+ {% endfor %} + {% else %} +
暂无内容
+ {% endif %} +
diff --git a/view/column/_text.html b/view/column/_text.html new file mode 100644 index 0000000..7b96e01 --- /dev/null +++ b/view/column/_text.html @@ -0,0 +1,19 @@ + +
+ {% if contentData.list and contentData.list.length > 0 %} + {% for item in contentData.list %} +
+
+ {% if item.attachment %} + {{item.title}} + {% else %} + {{item.title}} + {% endif %} +
+ {% if item.publish_date %}
{{item.publish_date}}
{% endif %} +
+ {% endfor %} + {% else %} +
暂无内容
+ {% endif %} +
diff --git a/view/column_index.html b/view/column_index.html new file mode 100644 index 0000000..21e548f --- /dev/null +++ b/view/column_index.html @@ -0,0 +1,71 @@ +{% extends "layout.html" %} + +{% block title %}{{column.name}} - {{siteConfig.site_name}}{% endblock %} + +{% block content %} + +
+
+
+

{{column.name}}

+ {% if column.name_en %} +
{{column.name_en}}
+ {% endif %} + {% if column.description %} +
{{column.description}}
+ {% endif %} +
+
+ + +{% if children and children.length > 0 %} +
+
+ {% for child in children %} + + {{child.name}} + + {% endfor %} +
+
+{% endif %} + + +
+
+ 您的位置: + 首页> + {{column.name}} + {% if currentChild %} + >{{currentChild.name}} + {% endif %} +
+
+ + +
+
+ {% if currentChild %} +

{{currentChild.name}}

+ {% endif %} + {% if contentData.type == 'page' %} + {% include "column/_page.html" %} + {% elif contentData.type == 'article' %} + {% include "column/_article.html" %} + {% elif contentData.type == 'image' %} + {% include "column/_image.html" %} + {% elif contentData.type == 'text' %} + {% include "column/_text.html" %} + {% elif contentData.type == 'person' %} + {% include "column/_person.html" %} + {% elif contentData.type == 'job' %} + {% include "column/_job.html" %} + {% else %} +
暂无内容
+ {% endif %} +
+
+ +{% include "common/_footer.html" %} +{% endblock %} diff --git a/view/common/_footer.html b/view/common/_footer.html index 056b953..3aa0d10 100644 --- a/view/common/_footer.html +++ b/view/common/_footer.html @@ -35,7 +35,7 @@