+ */ + const getArticles = async (params: ArticleListParams = {}): Promise => { + try { + const response = await fetchArticleList(params) + return response.data || [] + } catch (error) { + console.error('获取文章列表失败:', error) + return [] + } + } + + /** + * 获取推荐文章(按类型分组) + * @param types 文章类型数组 + * @param pageSize 每页数量 + * @returns Promise> + */ + const getRecommendedArticles = async ( + types: string[] = ['solution', 'developKnowledge', 'industryTrend'], + pageSize: number = 5 + ): Promise> => { + return await fetchRecommendedArticles(types, pageSize) + } + + return { + getArticles, + getRecommendedArticles, + getArticleTypeName + } +} diff --git a/app/composables/useImageStyles.ts b/app/composables/useImageStyles.ts new file mode 100644 index 0000000..5835063 --- /dev/null +++ b/app/composables/useImageStyles.ts @@ -0,0 +1,82 @@ +import {nextTick, watch, watchEffect, onMounted, type Ref} from 'vue' + +/** + * 图片样式处理 Composable + * 用于强制处理 v-html 内容中的图片样式 + */ +export const useImageStyles = ( + contentRef: Ref | (() => string), + containerSelector: string = '.article-content', + options: { + maxWidth?: string + margin?: string + display?: string + boxSizing?: string + } = {} +) => { + // 默认配置 + const defaultOptions = { + maxWidth: 'calc(100% - 0px)', + margin: '15px 0', + display: 'block', + boxSizing: 'border-box', + ...options + } + + /** + * 强制处理图片样式 + */ + const forceImageStyles = () => { + if (typeof document === 'undefined') return + + const images = document.querySelectorAll(`${containerSelector} img`) + images.forEach((img: any) => { + // 强制设置样式 + img.style.maxWidth = defaultOptions.maxWidth + img.style.height = 'auto' + img.style.width = 'auto' + img.style.display = defaultOptions.display + img.style.margin = defaultOptions.margin + img.style.boxSizing = defaultOptions.boxSizing + }) + } + + /** + * 处理图片样式的核心方法 + */ + const processImageStyles = () => { + nextTick(() => { + forceImageStyles() + }) + } + + /** + * 初始化图片样式处理 + */ + const initImageStyles = () => { + // 组件挂载时处理 + onMounted(() => { + processImageStyles() + }) + + // 监听内容变化 + if (typeof contentRef === 'function') { + // 如果是函数,使用 watchEffect 监听 + watchEffect(() => { + contentRef() // 触发函数执行 + processImageStyles() + }) + } else { + // 如果是 Ref,监听其值 + watch(contentRef, () => { + processImageStyles() + }, {deep: true}) + } + } + + return { + forceImageStyles, + processImageStyles, + initImageStyles + } +} diff --git a/app/config/api.ts b/app/config/api.ts new file mode 100644 index 0000000..7a40b3b --- /dev/null +++ b/app/config/api.ts @@ -0,0 +1,61 @@ +/** + * API配置文件 + * 统一管理所有API相关配置 + */ + +// API基础地址配置 +export const API_CONFIG = { + // 开发环境API地址 + BASE_URL: 'http://192.168.1.4:4101', + + // API端点配置 + ENDPOINTS: { + // 文章相关API + ARTICLE: { + LIST: '/app/owArticle/list', + GET: '/app/owArticle/get', + CREATE: '/app/owArticle/create', + UPDATE: '/app/owArticle/update', + DELETE: '/app/owArticle/delete' + } + }, + + // 请求配置 + REQUEST: { + TIMEOUT: 10000, // 请求超时时间(毫秒) + RETRY_COUNT: 3, // 重试次数 + HEADERS: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + } +} as const + +/** + * 获取完整的API URL + * @param endpoint API端点 + * @returns 完整的API URL + */ +export const getApiUrl = (endpoint: string): string => { + return `${API_CONFIG.BASE_URL}${endpoint}` +} + +/** + * 获取文章API URL + * @param action 操作类型 + * @param id 文章ID(可选) + * @returns 文章API URL + */ +export const getArticleApiUrl = (action: keyof typeof API_CONFIG.ENDPOINTS.ARTICLE, id?: string | number): string => { + const endpoint = API_CONFIG.ENDPOINTS.ARTICLE[action] + const baseUrl = getApiUrl(endpoint) + + if (id && (action === 'GET' || action === 'UPDATE' || action === 'DELETE')) { + return `${baseUrl}/${id}` + } + + return baseUrl +} + +// 导出默认配置 +export default API_CONFIG diff --git a/app/pages/news/[id].vue b/app/pages/news/[id].vue new file mode 100644 index 0000000..fed531c --- /dev/null +++ b/app/pages/news/[id].vue @@ -0,0 +1,103 @@ + + + + + + + + + diff --git a/public/img/news/banner2.png b/public/img/news/banner2.png new file mode 100644 index 0000000..ac535f1 Binary files /dev/null and b/public/img/news/banner2.png differ
=> { + try { + const response = await fetchArticleList(params) + return response.data || [] + } catch (error) { + console.error('获取文章列表失败:', error) + return [] + } + } + + /** + * 获取推荐文章(按类型分组) + * @param types 文章类型数组 + * @param pageSize 每页数量 + * @returns Promise> + */ + const getRecommendedArticles = async ( + types: string[] = ['solution', 'developKnowledge', 'industryTrend'], + pageSize: number = 5 + ): Promise> => { + return await fetchRecommendedArticles(types, pageSize) + } + + return { + getArticles, + getRecommendedArticles, + getArticleTypeName + } +} diff --git a/app/composables/useImageStyles.ts b/app/composables/useImageStyles.ts new file mode 100644 index 0000000..5835063 --- /dev/null +++ b/app/composables/useImageStyles.ts @@ -0,0 +1,82 @@ +import {nextTick, watch, watchEffect, onMounted, type Ref} from 'vue' + +/** + * 图片样式处理 Composable + * 用于强制处理 v-html 内容中的图片样式 + */ +export const useImageStyles = ( + contentRef: Ref | (() => string), + containerSelector: string = '.article-content', + options: { + maxWidth?: string + margin?: string + display?: string + boxSizing?: string + } = {} +) => { + // 默认配置 + const defaultOptions = { + maxWidth: 'calc(100% - 0px)', + margin: '15px 0', + display: 'block', + boxSizing: 'border-box', + ...options + } + + /** + * 强制处理图片样式 + */ + const forceImageStyles = () => { + if (typeof document === 'undefined') return + + const images = document.querySelectorAll(`${containerSelector} img`) + images.forEach((img: any) => { + // 强制设置样式 + img.style.maxWidth = defaultOptions.maxWidth + img.style.height = 'auto' + img.style.width = 'auto' + img.style.display = defaultOptions.display + img.style.margin = defaultOptions.margin + img.style.boxSizing = defaultOptions.boxSizing + }) + } + + /** + * 处理图片样式的核心方法 + */ + const processImageStyles = () => { + nextTick(() => { + forceImageStyles() + }) + } + + /** + * 初始化图片样式处理 + */ + const initImageStyles = () => { + // 组件挂载时处理 + onMounted(() => { + processImageStyles() + }) + + // 监听内容变化 + if (typeof contentRef === 'function') { + // 如果是函数,使用 watchEffect 监听 + watchEffect(() => { + contentRef() // 触发函数执行 + processImageStyles() + }) + } else { + // 如果是 Ref,监听其值 + watch(contentRef, () => { + processImageStyles() + }, {deep: true}) + } + } + + return { + forceImageStyles, + processImageStyles, + initImageStyles + } +} diff --git a/app/config/api.ts b/app/config/api.ts new file mode 100644 index 0000000..7a40b3b --- /dev/null +++ b/app/config/api.ts @@ -0,0 +1,61 @@ +/** + * API配置文件 + * 统一管理所有API相关配置 + */ + +// API基础地址配置 +export const API_CONFIG = { + // 开发环境API地址 + BASE_URL: 'http://192.168.1.4:4101', + + // API端点配置 + ENDPOINTS: { + // 文章相关API + ARTICLE: { + LIST: '/app/owArticle/list', + GET: '/app/owArticle/get', + CREATE: '/app/owArticle/create', + UPDATE: '/app/owArticle/update', + DELETE: '/app/owArticle/delete' + } + }, + + // 请求配置 + REQUEST: { + TIMEOUT: 10000, // 请求超时时间(毫秒) + RETRY_COUNT: 3, // 重试次数 + HEADERS: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + } +} as const + +/** + * 获取完整的API URL + * @param endpoint API端点 + * @returns 完整的API URL + */ +export const getApiUrl = (endpoint: string): string => { + return `${API_CONFIG.BASE_URL}${endpoint}` +} + +/** + * 获取文章API URL + * @param action 操作类型 + * @param id 文章ID(可选) + * @returns 文章API URL + */ +export const getArticleApiUrl = (action: keyof typeof API_CONFIG.ENDPOINTS.ARTICLE, id?: string | number): string => { + const endpoint = API_CONFIG.ENDPOINTS.ARTICLE[action] + const baseUrl = getApiUrl(endpoint) + + if (id && (action === 'GET' || action === 'UPDATE' || action === 'DELETE')) { + return `${baseUrl}/${id}` + } + + return baseUrl +} + +// 导出默认配置 +export default API_CONFIG diff --git a/app/pages/news/[id].vue b/app/pages/news/[id].vue new file mode 100644 index 0000000..fed531c --- /dev/null +++ b/app/pages/news/[id].vue @@ -0,0 +1,103 @@ + + + + + + + + + diff --git a/public/img/news/banner2.png b/public/img/news/banner2.png new file mode 100644 index 0000000..ac535f1 Binary files /dev/null and b/public/img/news/banner2.png differ