diff --git a/README.md b/README.md new file mode 100644 index 0000000..a41ed9d --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +图片目录地址 +git http://39.106.86.127:3000/hb_rd/front_dist.git +branch proj_icampus_dev + +powershell 软链形式 +``` +New-Item -ItemType Junction -Path "src/images" -Target C:\Users\kola\project\school-system\web_dist\images +``` diff --git a/dist.ps1 b/dist.ps1 new file mode 100644 index 0000000..9e09bb3 --- /dev/null +++ b/dist.ps1 @@ -0,0 +1,12 @@ +# Change to the src/images directory, or exit if the directory doesn't exist +Set-Location -Path C:\Users\kola\project\school-system\web_dist\images -ErrorAction Stop + +# 1. 添加当前目录所有文件(包含.gitignore忽略的文件也一并加入,等价svn --no-ignore --force) +git add --force . + +# 2. 提交,备注update +git commit -m "update" + +git push origin proj_icampus_dev + +ssh hb127 "cd /data1/wwwroot/default/highedu/web_dist_server/dist && git pull && exit" diff --git a/project.private.config.json b/project.private.config.json index 52fc110..3a43ce0 100644 --- a/project.private.config.json +++ b/project.private.config.json @@ -4,13 +4,83 @@ "miniprogram": { "list": [ { - "name": "活动报名结果页", - "pathName": "pages/actResult/index", + "name": "登录", + "pathName": "pages/login/index", "query": "", "scene": null, "launchMode": "default" }, { + "name": "我的评论", + "pathName": "pages/myComment/index", + "query": "", + "launchMode": "default", + "scene": null + }, + { + "name": "我的智能体", + "pathName": "pages/myAgent/index", + "query": "", + "launchMode": "default", + "scene": null + }, + { + "name": "我的活动", + "pathName": "pages/myAct/index", + "query": "", + "launchMode": "default", + "scene": null + }, + { + "name": "我的", + "pathName": "pages/my/index", + "query": "", + "launchMode": "default", + "scene": null + }, + { + "name": "智能体-评价", + "pathName": "pages/agentEva/index", + "query": "", + "launchMode": "default", + "scene": null + }, + { + "name": "智能体", + "pathName": "pages/agent/index", + "query": "", + "launchMode": "default", + "scene": null + }, + { + "name": "校园巴士", + "pathName": "pages/buses/index", + "query": "", + "launchMode": "default", + "scene": null + }, + { + "name": "对话历史", + "pathName": "pages/chatHistory/index", + "query": "", + "launchMode": "default", + "scene": null + }, + { + "name": "chat", + "pathName": "pages/chat/index", + "query": "", + "launchMode": "default", + "scene": null + }, + { + "name": "活动报名结果页", + "pathName": "pages/actResult/index", + "query": "", + "launchMode": "default", + "scene": null + }, + { "name": "创建活动结果页", "pathName": "pages/actAddResult/index", "query": "", diff --git a/src/app.json b/src/app.json index bde6e89..3278354 100644 --- a/src/app.json +++ b/src/app.json @@ -3,6 +3,9 @@ "pages/start/index", "pages/index/index", "pages/act/index", + "pages/buses/index", + "pages/agent/index", + "pages/my/index", "pages/notice/index", "pages/actDetail/index", "pages/actResult/index", @@ -10,7 +13,12 @@ "pages/actAddResult/index", "pages/noticeDetail/index", "pages/login/index", - "pages/my/index" + "pages/chat/index", + "pages/chatHistory/index", + "pages/agentEva/index", + "pages/myAct/index", + "pages/myAgent/index", + "pages/myComment/index" ], "window": { "backgroundTextStyle": "light", @@ -38,6 +46,10 @@ "text": "通知" }, { + "pagePath": "pages/agent/index", + "text": "活动" + }, + { "pagePath": "pages/my/index", "text": "我的" } diff --git a/src/app.ts b/src/app.ts index 6b0f5c9..4f656b1 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,9 +1,9 @@ import page from '@/utils/page' + import { request } from './api/request' -import { parseScene } from './utils/util' +import { parseScene } from './utils/util' const dayjs = require('dayjs') - const licia = require('miniprogram-licia') require('/utils/dayjs/day-zh-cn.js') const relativeTime = require('/utils/dayjs/relativeTime.js') @@ -14,7 +14,7 @@ App({ globalData: { url: '', upFileUrl: '', - imageUrl: '', + imageUrl: 'https://app.gohighedu.cn/images', Timestamp: new Date().getTime(), diff --git a/src/components/svg-icon/README.md b/src/components/svg-icon/README.md new file mode 100644 index 0000000..a41ac4b --- /dev/null +++ b/src/components/svg-icon/README.md @@ -0,0 +1,164 @@ +# SVG Icon 组件 + +微信小程序 SVG 组件,支持对 SVG 重新着色。 + +## 组件路径 + +`/components/svg-icon/index` + +## 安装 + +在页面或全局 `json` 配置中引入组件: + +```json +{ + "usingComponents": { + "svg-icon": "/components/svg-icon/index" + } +} +``` + +## 基础用法 + +```xml + + + + + +``` + +## 单颜色重新着色 + +传入 `color` 时,会对 SVG 中所有声明 `fill/stroke` 的元素统一着色: + +```xml + +``` + +## 多颜色重新着色 + +### 数组形式(按顺序替换) + +以数组形式传入 `colors` 时,依照数组中的颜色顺序,对 SVG 中所有声明 `fill/stroke` 的元素按顺序重新着色: + +```ts +// page.ts +Page({ + data: { + colorsArray: ['#ff0000', '#00ff00', '#0000ff'], + }, +}) +``` + +```xml + + +``` + +### 对象形式(按键值关系替换) + +以对象形式传入 `colors` 时,依照对象中的键值关系,对 SVG 中所有声明 `fill/stroke` 的元素按对应关系重新着色: + +```ts +// page.ts +Page({ + data: { + colorsObject: { + black: '#ff0000', + '#fff': '#00ff00', + '#808080': '#cdcdcd', + }, + }, +}) +``` + +```xml + + +``` + +## 组合重新着色 + +同时传入 `color` 和 `colors` 组合搭配,既能为指定元素重新着色,也能为其余未指定元素统一着色: + +```ts +// page.ts +Page({ + data: { + colorsArray: ['#ff0000', '#00ff00'], + }, +}) +``` + +```xml + + +``` + +## 网络资源 + +支持传入网络 SVG 资源地址: + +```xml + +``` + +**注意**:当 src 传入网络资源并重新着色时,请将网络资源的域名配置于小程序的 `downloadFile` 合法域名中。 + +## API + +### Properties + +| 参数 | 说明 | 类型 | 默认值 | 必填 | +| --- | --- | --- | --- | --- | +| `src` | SVG 资源地址(支持本地路径、临时路径、网络资源) | `string` | `''` | 是 | +| `color` | SVG 单一颜色(对所有 fill/stroke 元素统一着色) | `string` | `''` | 否 | +| `colors` | SVG 多颜色配置(支持数组或对象) | `array \| object` | `null` | 否 | +| `mode` | SVG 裁剪、缩放模式(与 `image` 标签相同) | `string` | `''` | 否 | + +### Events + +| 事件名 | 说明 | 回调参数 | +| --- | --- | --- | +| `bind:error` | 当错误发生时触发 | `event.detail = Error \| { errMsg }` | +| `bind:load` | 当图片载入完毕时触发 | `event.detail = { height, width }` | + +### External Classes + +| 类名 | 说明 | +| --- | --- | +| `image-class` | `image` 节点样式类 | + +## mode 可选值 + +与微信小程序 `image` 组件的 `mode` 属性相同: + +| 值 | 说明 | +| --- | --- | +| `scaleToFill` | 不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素 | +| `aspectFit` | 保持纵横比缩放图片,使图片的长边能完全显示出来 | +| `aspectFill` | 保持纵横比缩放图片,只保证图片的短边能完全显示出来 | +| `widthFix` | 宽度不变,高度自动变化,保持原图宽高比不变 | +| `heightFix` | 高度不变,宽度自动变化,保持原图宽高比不变 | +| `top` | 不缩放图片,只显示图片的顶部区域 | +| `bottom` | 不缩放图片,只显示图片的底部区域 | +| `center` | 不缩放图片,只显示图片的中间区域 | +| `left` | 不缩放图片,只显示图片的左边区域 | +| `right` | 不缩放图片,只显示图片的右边区域 | +| `top left` | 不缩放图片,只显示图片的左上边区域 | +| `top right` | 不缩放图片,只显示图片的右上边区域 | +| `bottom left` | 不缩放图片,只显示图片的左下边区域 | +| `bottom right` | 不缩放图片,只显示图片的右下边区域 | + +## 实现原理 + +1. 读取 SVG 文件内容(本地文件或网络文件) +2. 通过正则替换 SVG 中的 `fill/stroke` 属性值来实现改色 +3. 将修改后的 SVG 内容转为 base64 格式,作为 `image` 的 `src` + +## 注意事项 + +- SVG 文件必须包含 `fill` 或 `stroke` 属性才能被改色 +- 网络资源需要配置 `downloadFile` 合法域名 +- 组件会缓存下载的网络资源,避免重复下载 \ No newline at end of file diff --git a/src/components/svg-icon/base64.ts b/src/components/svg-icon/base64.ts new file mode 100644 index 0000000..8c18858 --- /dev/null +++ b/src/components/svg-icon/base64.ts @@ -0,0 +1,34 @@ +/** + * Base64 编码工具 + * 用于将 SVG 内容转为 base64 格式 + */ + +// Base64 字符映射表 +const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + +/** + * 将字符串编码为 Base64 + * @param str 要编码的字符串 + * @returns Base64 编码后的字符串 + */ +export function encode(str: string): string { + let result = '' + const len = str.length + const remainder = len % 3 + + for (let i = 0; i < len - remainder; i += 3) { + const n = (str.charCodeAt(i) << 16) | (str.charCodeAt(i + 1) << 8) | str.charCodeAt(i + 2) + result += CHARS[(n >> 18) & 63] + CHARS[(n >> 12) & 63] + CHARS[(n >> 6) & 63] + CHARS[n & 63] + } + + // 处理剩余字符 + if (remainder === 1) { + const n = str.charCodeAt(len - 1) + result += `${CHARS[(n >> 2) & 63] + CHARS[(n << 4) & 63] }==` + } else if (remainder === 2) { + const n = (str.charCodeAt(len - 2) << 8) | str.charCodeAt(len - 1) + result += `${CHARS[(n >> 10) & 63] + CHARS[(n >> 4) & 63] + CHARS[(n << 2) & 63] }=` + } + + return result +} \ No newline at end of file diff --git a/src/components/svg-icon/index.json b/src/components/svg-icon/index.json new file mode 100644 index 0000000..e8cfaaf --- /dev/null +++ b/src/components/svg-icon/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/src/components/svg-icon/index.scss b/src/components/svg-icon/index.scss new file mode 100644 index 0000000..e65039e --- /dev/null +++ b/src/components/svg-icon/index.scss @@ -0,0 +1,7 @@ +/** + * SVG Icon 组件样式 + */ + +.svg-icon { + display: block; +} \ No newline at end of file diff --git a/src/components/svg-icon/index.ts b/src/components/svg-icon/index.ts new file mode 100644 index 0000000..a86ea35 --- /dev/null +++ b/src/components/svg-icon/index.ts @@ -0,0 +1,156 @@ +/** + * SVG Icon 组件 + * 支持对 SVG 重新着色 + */ + +import { encode } from './base64' + +const fs = wx.getFileSystemManager() + +// 临时文件缓存(网络资源下载后缓存) +const tempFileMap = new Map() + +/** + * 同步下载网络文件 + * @param url 网络资源地址 + * @returns 下载结果 + */ +function downloadFileSync(url: string): Promise { + return new Promise((resolve, reject) => { + wx.downloadFile({ + url, + success: resolve, + fail: reject, + }) + }) +} + +Component({ + options: { + styleIsolation: 'shared', + }, + + externalClasses: ['image-class'], + + properties: { + /** SVG 资源地址(支持本地路径、临时路径、网络资源) */ + src: { + type: String, + value: '', + }, + /** SVG 单一颜色(对所有 fill/stroke 元素统一着色) */ + color: { + type: String, + value: '', + }, + /** SVG 多颜色配置(支持数组或对象) */ + colors: { + type: null, + value: null, + }, + /** SVG 裁剪、缩放模式(与 image 标签相同) */ + mode: { + type: String, + value: '', + }, + }, + + observers: { + 'src, color, colors': async function (src: string, color: string, colors: unknown) { + try { + // 如果需要改色 + if (color || (colors && ((Array.isArray(colors) && colors.length > 0) || (typeof colors === 'object' && Object.keys(colors).length > 0)))) { + let svgData: string + + // 判断是否为网络资源(排除开发工具临时路径 http://tmp/) + if (/^https?:\/\//.test(src) && !/^http:\/\/tmp\//.test(src)) { + // 网络资源需要先下载 + let tempFilePath = tempFileMap.get(src) + try { + if (!tempFilePath) throw new Error('未缓存') + // 检查临时文件是否存在 + fs.accessSync(tempFilePath) + } catch { + // 下载文件 + const downloadResult = await downloadFileSync(src) + tempFilePath = downloadResult.tempFilePath + // 缓存临时文件路径 + tempFileMap.set(src, tempFilePath) + } + // 读取文件内容 + svgData = fs.readFileSync(tempFilePath, 'utf8') as string + } else { + // 本地资源直接读取 + svgData = fs.readFileSync(src, 'utf8') as string + } + + // 处理颜色配置 + const colorsConfig = colors || {} + + // 替换 SVG 中的 fill/stroke 属性 + if (/(?:fill|stroke)=".*?"/.test(svgData)) { + let colorIndex = 0 + svgData = svgData.replace(/(?:fill|stroke)=".*?"/g, (matched) => { + // 获取原本颜色值 + const originalColor = matched.slice(matched.indexOf('"') + 1, -1) + + // 计算替换颜色 + let replaceColor: string + if (Array.isArray(colorsConfig)) { + // 数组形式:按顺序替换 + replaceColor = colorsConfig[colorIndex++] || color || originalColor + } else { + // 对象形式:按键值关系替换 + replaceColor = colorsConfig[originalColor] || colorsConfig[colorIndex++] || color || originalColor + } + + // 返回替换后的属性 + if (/fill/.test(matched)) return `fill="${replaceColor}"` + if (/stroke/.test(matched)) return `stroke="${replaceColor}"` + return `fill="${replaceColor}"` + }) + } + + // 设置默认底色(SVG 根元素) + const defaultColor = (typeof colorsConfig === 'object' && !Array.isArray(colorsConfig) && (colorsConfig['#000'] || colorsConfig['#000000'] || colorsConfig.black)) || color + if (defaultColor && !/fill=".*?"/.test(svgData.slice(0, svgData.indexOf('>')))) { + svgData = svgData.replace(/ + \ No newline at end of file diff --git a/src/components/uploadFile/README.md b/src/components/uploadFile/README.md new file mode 100644 index 0000000..5380ee6 --- /dev/null +++ b/src/components/uploadFile/README.md @@ -0,0 +1,158 @@ +# Upload 上传组件 API 文档 + +## 组件路径 + +`@/components/uploadFile/index` + +## 说明 + +上传接口地址固定为 `app.globalData.upFileUrl`,组件内部自动附加 `loginState`,无需外部传入。 + +## Properties 入参 + +| 参数 | 说明 | 类型 | 默认值 | 必填 | +| --- | --- | --- | --- | --- | +| `maxCount` | 最大上传文件数 | `Number` | `9` | 否 | +| `maxSize` | 单文件最大限制(byte) | `Number` | `10485760`(10MB) | 否 | +| `accept` | 允许的文件类型数组,可选值:`'image'` \| `'video'` \| `'file'` | `Array` | `['image']` | 否 | +| `extensions` | 自定义文件后缀(仅 accept 含 `'file'` 时生效),如 `['.pdf', '.doc']` | `Array` | `[]` | 否 | +| `readonly` | 只读模式(不显示上传和删除按钮) | `Boolean` | `false` | 否 | +| `useSlot` | 是否使用自定义上传区域插槽 | `Boolean` | `false` | 否 | +| `fileList` | 已有文件列表(用于回显) | `Array` | `[]` | 否 | + +### fileList 数据结构 + +```ts +{ + uid: string // 文件唯一标识 + url: string // 文件路径 + type: string // 文件类型: 'image' | 'video' | 'file' + name: string // 文件名 + size: number // 文件大小(byte) +} +``` + +## Events 事件 + +| 事件名 | 说明 | 回调参数 | +| --- | --- | --- | +| `bind:select` | 选中本地文件后触发 | `{ files: UploadFile[] }` | +| `bind:success` | 单文件上传完成 | `{ file: UploadFile, response: any }` | +| `bind:error` | 上传失败 | `{ file: UploadFile, error: Error }` | +| `bind:remove` | 删除文件 | `{ file: UploadFile, fileList: UploadFile[] }` | + +### 事件返回的 UploadFile 结构 + +```ts +{ + uid: string // 文件唯一标识 + url: string // 文件路径(上传成功后为远程 url) + type: 'image' | 'video' | 'file' // 文件类型 + name: string // 文件名 + size: number // 文件大小(byte) + status: 'pending' | 'uploading' | 'success' | 'error' // 上传状态 + progress: number // 上传进度 0-100 +} +``` + +## Slots 插槽 + +| 插槽名 | 说明 | +| --- | --- | +| `upload-area` | 自定义上传区域布局,需同时设置 `useSlot="{{true}}"`。使用时上传触发器尺寸自适应 slot 内容 | + +## CSS 变量 + +| 变量名 | 说明 | 默认值 | +| --- | --- | --- | +| `--upload-bg` | 上传区域背景色 | `#f7f8fa` | +| `--upload-border` | 边框颜色 | `#e5e7eb` | +| `--upload-text` | 主文字颜色 | `#1f2937` | +| `--upload-text-secondary` | 次要文字颜色 | `#9ca3af` | +| `--upload-primary` | 主题色 | `#3b82f6` | +| `--upload-error` | 错误色 | `#ef4444` | +| `--upload-mask` | 遮罩颜色 | `rgba(0, 0, 0, 0.5)` | +| `--upload-radius` | 圆角大小 | `16rpx` | +| `--upload-size` | 文件项尺寸(仅默认上传框生效,slot 模式自适应) | `160rpx` | + +## 使用案例 + +### 案例 1:使用默认上传框 + +```json +// page.json +{ + "usingComponents": { + "upload-file": "@/components/uploadFile/index" + } +} +``` + +```xml + + +``` + +```ts +// page.ts +Page({ + onUploadSelect(e: WechatMiniprogram.CustomEvent) { + console.log('选中文件', e.detail.files) + }, + onUploadSuccess(e: WechatMiniprogram.CustomEvent) { + console.log('上传成功', e.detail.file, e.detail.response) + }, + onUploadError(e: WechatMiniprogram.CustomEvent) { + console.log('上传失败', e.detail.file, e.detail.error) + }, + onUploadRemove(e: WechatMiniprogram.CustomEvent) { + console.log('删除文件', e.detail.file, e.detail.fileList) + }, +}) +``` + +### 案例 2:使用 slot 自定义上传区域 + +```xml + + + + + 点击上传 + + +``` + +```scss +// page.scss +.custom-upload-area { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 12rpx; + + image { + width: 64rpx; + height: 64rpx; + } + + text { + font-size: 24rpx; + color: #9ca3af; + } +} +``` diff --git a/src/components/uploadFile/index.json b/src/components/uploadFile/index.json new file mode 100644 index 0000000..a89ef4d --- /dev/null +++ b/src/components/uploadFile/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} diff --git a/src/components/uploadFile/index.scss b/src/components/uploadFile/index.scss new file mode 100644 index 0000000..1efcfa2 --- /dev/null +++ b/src/components/uploadFile/index.scss @@ -0,0 +1,272 @@ +/** + * Upload 上传组件样式 + * 简约通用,颜色通过 CSS 变量定义,方便外部覆盖 + */ + +/* CSS 变量定义,外部可通过 page 或父级覆盖 */ +page { + --upload-bg: #f7f8fa; + --upload-border: #e5e7eb; + --upload-text: #1f2937; + --upload-text-secondary: #9ca3af; + --upload-primary: #3b82f6; + --upload-error: #ef4444; + --upload-mask: rgba(0, 0, 0, 0.5); + --upload-radius: 16rpx; + --upload-size: 160rpx; + --upload-preview-height: 160rpx; // 预览项高度(独立控制) +} + +.upload { + width: 100%; + + .upload-list { + display: flex; + flex-wrap: wrap; + gap: 16rpx; + } + + .upload-item { + position: relative; + width: 100%; + height: var(--upload-preview-height); + border-radius: var(--upload-radius); + overflow: hidden; + + &--file { + width: 100%; + height: auto; + min-height: var(--upload-preview-height); + } + } + + .upload-preview { + width: 100%; + height: 100%; + position: relative; + + &--file { + display: flex; + align-items: center; + gap: 16rpx; + padding: 20rpx; + background: var(--upload-bg); + border-radius: var(--upload-radius); + } + + .upload-preview-media { + width: 100%; + height: 100%; + display: block; + } + + .upload-preview-play { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; + background: var(--upload-mask); + + .upload-preview-play-icon { + width: 0; + height: 0; + border-style: solid; + border-width: 16rpx 0 16rpx 28rpx; + border-color: transparent transparent transparent #fff; + margin-left: 8rpx; + } + } + } + + /* 文件类型预览 */ + .upload-file-icon { + width: 80rpx; + height: 96rpx; + background: #fff; + border: 2rpx solid var(--upload-border); + border-radius: 8rpx; + position: relative; + display: flex; + align-items: flex-end; + justify-content: center; + padding-bottom: 12rpx; + flex-shrink: 0; + + .upload-file-icon-corner { + position: absolute; + top: 0; + right: 0; + width: 24rpx; + height: 24rpx; + background: var(--upload-border); + border-radius: 0 6rpx 0 8rpx; + } + + .upload-file-icon-text { + font-size: 16rpx; + color: var(--upload-text-secondary); + max-width: 60rpx; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + .upload-file-name { + flex: 1; + font-size: 26rpx; + color: var(--upload-text); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + /* 上传中遮罩 */ + .upload-mask { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: var(--upload-mask); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 12rpx; + + .upload-progress { + width: 70%; + height: 6rpx; + background: rgba(255, 255, 255, 0.3); + border-radius: 3rpx; + overflow: hidden; + + .upload-progress-bar { + height: 100%; + background: #fff; + border-radius: 3rpx; + transition: width 0.2s ease; + } + } + + .upload-progress-text { + font-size: 24rpx; + color: #fff; + } + + &--error { + .upload-error-text { + font-size: 24rpx; + color: #fff; + } + + .upload-retry { + padding: 8rpx 24rpx; + border: 2rpx solid #fff; + border-radius: 24rpx; + + .upload-retry-text { + font-size: 24rpx; + color: #fff; + } + } + } + } + + /* 删除按钮 */ + .upload-remove { + position: absolute; + top: -8rpx; + right: -8rpx; + width: 36rpx; + height: 36rpx; + background: var(--upload-mask); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + z-index: 2; + + .upload-remove-icon { + position: relative; + width: 18rpx; + height: 18rpx; + + &::before, + &::after { + content: ''; + position: absolute; + top: 50%; + left: 0; + width: 100%; + height: 2rpx; + background: #fff; + transform-origin: center; + } + + &::before { + transform: translateY(-50%) rotate(45deg); + } + + &::after { + transform: translateY(-50%) rotate(-45deg); + } + } + } + + /* 上传触发器 */ + .upload-trigger { + width: var(--upload-size); + height: var(--upload-size); + + &--slot { + width: 100%; // slot 模式下宽度撑满父容器 + height: auto; // 高度自适应 slot 内容 + } + + .upload-trigger-default { + width: 100%; + height: 100%; + background: var(--upload-bg); + border-radius: var(--upload-radius); + display: flex; + align-items: center; + justify-content: center; + border: 2rpx dashed var(--upload-border); + box-sizing: border-box; + + .upload-trigger-icon { + position: relative; + width: 48rpx; + height: 48rpx; + + &::before, + &::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + background: var(--upload-text-secondary); + border-radius: 2rpx; + } + + &::before { + width: 48rpx; + height: 4rpx; + transform: translate(-50%, -50%); + } + + &::after { + width: 4rpx; + height: 48rpx; + transform: translate(-50%, -50%); + } + } + } + } +} diff --git a/src/components/uploadFile/index.ts b/src/components/uploadFile/index.ts new file mode 100644 index 0000000..dbad1d7 --- /dev/null +++ b/src/components/uploadFile/index.ts @@ -0,0 +1,363 @@ +/** + * Upload 上传组件 + * 支持图片/视频/文件上传,多文件选择,实时进度条,本地预览,删除附件,失败重试 + */ + +/** 单个文件对象 */ +interface UploadFile { + /** 文件唯一标识 */ + uid: string + /** 本地临时路径或上传后的远程 url */ + url: string + /** 文件类型: image | video | file */ + type: string + /** 文件名 */ + name: string + /** 文件大小(byte) */ + size: number + /** 上传状态: pending | uploading | success | error */ + status: 'pending' | 'uploading' | 'success' | 'error' + /** 上传进度 0-100 */ + progress: number + /** 上传任务对象 */ + _task?: WechatMiniprogram.UploadTask +} + +/** 文件大小格式化 */ +function formatSize(size: number): string { + if (size < 1024) return `${size}B` + if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)}KB` + return `${(size / (1024 * 1024)).toFixed(1)}MB` +} + +Component({ + options: { + multipleSlots: true, + styleIsolation: 'shared', // 允许外部样式影响组件内部 + }, + + properties: { + /** 最大上传文件数,默认 9 */ + maxCount: { + type: Number, + value: 9, + }, + /** 单文件最大限制(byte),默认 10MB */ + maxSize: { + type: Number, + value: 10 * 1024 * 1024, + }, + /** 允许的文件类型数组: 'image' | 'video' | 'file' */ + accept: { + type: Array, + value: ['image'], + }, + /** 自定义文件后缀(仅 accept 含 file 时生效),如 ['.pdf', '.doc', '.docx'] */ + extensions: { + type: Array, + value: [], + }, + /** 只读模式(不显示上传和删除按钮) */ + readonly: { + type: Boolean, + value: false, + }, + /** 是否使用自定义上传区域插槽 */ + useSlot: { + type: Boolean, + value: false, + }, + /** 已有文件列表(用于回显) */ + fileList: { + type: Array, + value: [], + }, + }, + + data: { + /** 内部文件列表 */ + _fileList: [] as UploadFile[], + }, + + lifetimes: { + attached() { + // 初始化已有文件列表 + if (this.properties.fileList.length > 0) { + this.setData({ + _fileList: this.properties.fileList.map((item: any) => ({ + uid: item.uid || `init_${Date.now()}_${Math.random()}`, + url: item.url, + type: item.type || 'image', + name: item.name || '', + size: item.size || 0, + status: 'success', + progress: 100, + })), + }) + } + }, + }, + + methods: { + /** + * 选择文件 + */ + onChooseFile() { + if (this.data.readonly) return + const { _fileList, maxCount } = this.data + const remaining = maxCount - _fileList.length + if (remaining <= 0) { + wx.showToast({ title: `最多上传${maxCount}个文件`, icon: 'none' }) + return + } + + const accept = this.properties.accept as string[] + // 如果包含 image 或 video,使用 wx.chooseMedia + const hasImage = accept.includes('image') + const hasVideo = accept.includes('video') + const hasFile = accept.includes('file') + + if (hasFile && !hasImage && !hasVideo) { + // 纯文件上传 + this.chooseFileOnly(remaining) + } else if (hasImage || hasVideo) { + // 图片/视频 + this.chooseMedia(remaining, hasImage, hasVideo, hasFile) + } + }, + + /** + * 选择图片/视频 + */ + chooseMedia(remaining: number, hasImage: boolean, hasVideo: boolean, hasFile: boolean) { + const mediaType: string[] = [] + if (hasImage) mediaType.push('image') + if (hasVideo) mediaType.push('video') + + wx.chooseMedia({ + count: remaining, + mediaType: mediaType as ('image' | 'video')[], + sourceType: ['album', 'camera'], + camera: 'back', + success: (res) => { + const files = res.tempFiles.map((f) => { + const isVideo = f.fileType === 'video' + return { + uid: `file_${Date.now()}_${Math.random()}`, + url: f.tempFilePath, + type: isVideo ? 'video' : 'image', + name: f.tempFilePath.split('/').pop() || '', + size: f.size, + status: 'pending' as const, + progress: 0, + } + }) + this.afterChoose(files) + }, + fail: (err) => { + if (!err.errMsg.includes('cancel')) { + wx.showToast({ title: '选择文件失败', icon: 'none' }) + } + }, + }) + + // 如果同时支持文件,提示用户 + if (hasFile) { + // 在 chooseMedia 之后无法同时选择文件,这里不做额外处理 + // 如果需要同时支持,用户可分多次选择 + } + }, + + /** + * 仅选择文件 + */ + chooseFileOnly(remaining: number) { + const extensions = this.properties.extensions as string[] + wx.chooseMessageFile({ + count: remaining, + type: extensions.length > 0 ? 'file' : 'file', + extension: extensions.length > 0 ? extensions : undefined, + success: (res) => { + const files: UploadFile[] = res.tempFiles.map((f) => ({ + uid: `file_${Date.now()}_${Math.random()}`, + url: f.path, + type: 'file', + name: f.name, + size: f.size, + status: 'pending', + progress: 0, + })) + this.afterChoose(files) + }, + fail: (err) => { + if (!err.errMsg.includes('cancel')) { + wx.showToast({ title: '选择文件失败', icon: 'none' }) + } + }, + }) + }, + + /** + * 选择文件后的校验和上传 + */ + afterChoose(files: UploadFile[]) { + // 校验文件大小 + const validFiles: UploadFile[] = [] + const maxSize = this.properties.maxSize + for (const file of files) { + if (file.size > maxSize) { + wx.showToast({ + title: `${file.name || '文件'}超过${formatSize(maxSize)}`, + icon: 'none', + }) + continue + } + validFiles.push(file) + } + + if (validFiles.length === 0) return + + // 触发 onSelect 事件 + this.triggerEvent('select', { files: validFiles }) + + // 更新文件列表 + const newList = [...this.data._fileList, ...validFiles] + this.setData({ _fileList: newList }) + + // 逐个上传 + validFiles.forEach((file) => { + this.uploadFile(file) + }) + }, + + /** + * 上传单个文件 + */ + uploadFile(file: UploadFile) { + const app = getApp() + const action = app.globalData.upFileUrl + const loginState = app.globalData.loginState + + this.updateFile(file.uid, { status: 'uploading', progress: 0 }) + + const task = wx.uploadFile({ + url: action, + filePath: file.url, + name: 'file', + header: { loginState }, + formData: { loginState }, + success: (res) => { + // 尝试解析返回数据 + let data: any + try { + data = JSON.parse(res.data) + } catch { + data = res.data + } + this.updateFile(file.uid, { + status: 'success', + progress: 100, + url: data.url || data.data?.url || file.url, + }) + this.triggerEvent('success', { file: this.getFile(file.uid), response: data }) + }, + fail: (err) => { + this.updateFile(file.uid, { status: 'error', progress: 0 }) + this.triggerEvent('error', { file: this.getFile(file.uid), error: err }) + }, + }) + + // 监听上传进度 + task.onProgressUpdate((res) => { + this.updateFile(file.uid, { progress: res.progress }) + }) + + // 保存 task 引用 + this.updateFile(file.uid, { _task: task }) + }, + + /** + * 重试上传 + */ + onRetry(e: WechatMiniprogram.BaseEvent) { + const { uid } = e.currentTarget.dataset + const file = this.getFile(uid) + if (file && file.status === 'error') { + this.uploadFile(file) + } + }, + + /** + * 删除文件 + */ + onRemove(e: WechatMiniprogram.BaseEvent) { + const { uid } = e.currentTarget.dataset + const file = this.getFile(uid) + if (!file) return + + // 取消上传任务 + if (file._task) { + file._task.abort() + } + + const newList = this.data._fileList.filter((f) => f.uid !== uid) + this.setData({ _fileList: newList }) + this.triggerEvent('remove', { file, fileList: newList }) + }, + + /** + * 预览图片 + */ + onPreviewImage(e: WechatMiniprogram.BaseEvent) { + const { uid } = e.currentTarget.dataset + const file = this.getFile(uid) + if (!file) return + + const imageList = this.data._fileList.filter((f) => f.type === 'image').map((f) => f.url) + wx.previewImage({ + current: file.url, + urls: imageList, + }) + }, + + /** + * 预览视频 + */ + onPreviewVideo(e: WechatMiniprogram.BaseEvent) { + const { uid } = e.currentTarget.dataset + const file = this.getFile(uid) + if (!file) return + wx.previewMedia({ + sources: [{ url: file.url, type: 'video' }], + current: 0, + }) + }, + + /** + * 更新单个文件状态 + */ + updateFile(uid: string, patch: Partial) { + const list = this.data._fileList.map((f) => { + if (f.uid === uid) { + return { ...f, ...patch } + } + return f + }) + this.setData({ _fileList: list }) + }, + + /** + * 获取单个文件 + */ + getFile(uid: string): UploadFile | undefined { + return this.data._fileList.find((f) => f.uid === uid) + }, + + /** + * 获取当前文件列表(供外部调用) + */ + getFileList(): UploadFile[] { + return this.data._fileList + }, + }, +}) diff --git a/src/components/uploadFile/index.wxml b/src/components/uploadFile/index.wxml new file mode 100644 index 0000000..f06131f --- /dev/null +++ b/src/components/uploadFile/index.wxml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + {{item.name}} + + {{item.name}} + + + + + + + + {{item.progress}}% + + + + + 上传失败 + + 重试 + + + + + + + + + + + + + + + + + + + + diff --git a/src/custom-tab-bar/index.ts b/src/custom-tab-bar/index.ts index 2e5663d..198afdf 100644 --- a/src/custom-tab-bar/index.ts +++ b/src/custom-tab-bar/index.ts @@ -29,7 +29,7 @@ Component({ iconActive: 'tabbar13', }, { - pagePath: '/pages/knowledge/index', + pagePath: '/pages/agent/index', text: '智能体', icon: 'tabbar4', iconActive: 'tabbar14', diff --git a/src/images/bg3.png b/src/images/bg3.png new file mode 100644 index 0000000..3bbbae9 Binary files /dev/null and b/src/images/bg3.png differ diff --git a/src/images/bg4.png b/src/images/bg4.png new file mode 100644 index 0000000..e09a7e0 Binary files /dev/null and b/src/images/bg4.png differ diff --git a/src/images/bg5.png b/src/images/bg5.png new file mode 100644 index 0000000..078e5eb Binary files /dev/null and b/src/images/bg5.png differ diff --git a/src/images/bg6.png b/src/images/bg6.png new file mode 100644 index 0000000..c5fa6d7 Binary files /dev/null and b/src/images/bg6.png differ diff --git a/src/images/gif1.gif b/src/images/gif1.gif new file mode 100644 index 0000000..c93f4b5 Binary files /dev/null and b/src/images/gif1.gif differ diff --git a/src/images/icon43.png b/src/images/icon43.png new file mode 100644 index 0000000..de62f46 Binary files /dev/null and b/src/images/icon43.png differ diff --git a/src/images/icon44.png b/src/images/icon44.png new file mode 100644 index 0000000..403e456 Binary files /dev/null and b/src/images/icon44.png differ diff --git a/src/images/icon45.png b/src/images/icon45.png new file mode 100644 index 0000000..bd4f76c Binary files /dev/null and b/src/images/icon45.png differ diff --git a/src/images/icon46.png b/src/images/icon46.png new file mode 100644 index 0000000..fb7b2fc Binary files /dev/null and b/src/images/icon46.png differ diff --git a/src/images/icon47.png b/src/images/icon47.png new file mode 100644 index 0000000..24aaabe Binary files /dev/null and b/src/images/icon47.png differ diff --git a/src/images/icon48.png b/src/images/icon48.png new file mode 100644 index 0000000..0fa2d42 Binary files /dev/null and b/src/images/icon48.png differ diff --git a/src/images/icon49.png b/src/images/icon49.png new file mode 100644 index 0000000..e7f1391 Binary files /dev/null and b/src/images/icon49.png differ diff --git a/src/images/icon50.png b/src/images/icon50.png new file mode 100644 index 0000000..11309ab Binary files /dev/null and b/src/images/icon50.png differ diff --git a/src/images/icon51.png b/src/images/icon51.png new file mode 100644 index 0000000..39905db Binary files /dev/null and b/src/images/icon51.png differ diff --git a/src/images/icon52.png b/src/images/icon52.png new file mode 100644 index 0000000..05d1515 Binary files /dev/null and b/src/images/icon52.png differ diff --git a/src/images/icon53.png b/src/images/icon53.png new file mode 100644 index 0000000..69d293a Binary files /dev/null and b/src/images/icon53.png differ diff --git a/src/images/icon54.png b/src/images/icon54.png new file mode 100644 index 0000000..3c183c4 Binary files /dev/null and b/src/images/icon54.png differ diff --git a/src/images/icon55.png b/src/images/icon55.png new file mode 100644 index 0000000..cb3e684 Binary files /dev/null and b/src/images/icon55.png differ diff --git a/src/images/icon56.png b/src/images/icon56.png new file mode 100644 index 0000000..46c46c7 Binary files /dev/null and b/src/images/icon56.png differ diff --git a/src/images/icon57.png b/src/images/icon57.png new file mode 100644 index 0000000..bbbd6ac Binary files /dev/null and b/src/images/icon57.png differ diff --git a/src/images/icon58.png b/src/images/icon58.png new file mode 100644 index 0000000..0aa2792 Binary files /dev/null and b/src/images/icon58.png differ diff --git a/src/images/icon59.png b/src/images/icon59.png new file mode 100644 index 0000000..8dc88e4 Binary files /dev/null and b/src/images/icon59.png differ diff --git a/src/images/icon60.png b/src/images/icon60.png new file mode 100644 index 0000000..fa98f78 Binary files /dev/null and b/src/images/icon60.png differ diff --git a/src/images/icon61.png b/src/images/icon61.png new file mode 100644 index 0000000..0d304d3 Binary files /dev/null and b/src/images/icon61.png differ diff --git a/src/images/icon62.png b/src/images/icon62.png new file mode 100644 index 0000000..a58a09d Binary files /dev/null and b/src/images/icon62.png differ diff --git a/src/images/icon63.png b/src/images/icon63.png new file mode 100644 index 0000000..9010edb Binary files /dev/null and b/src/images/icon63.png differ diff --git a/src/images/icon64.png b/src/images/icon64.png new file mode 100644 index 0000000..7c2305a Binary files /dev/null and b/src/images/icon64.png differ diff --git a/src/images/icon65.png b/src/images/icon65.png new file mode 100644 index 0000000..30e0748 Binary files /dev/null and b/src/images/icon65.png differ diff --git a/src/images/icon66.png b/src/images/icon66.png new file mode 100644 index 0000000..98100bb Binary files /dev/null and b/src/images/icon66.png differ diff --git a/src/images/icon67.png b/src/images/icon67.png new file mode 100644 index 0000000..e33ee57 Binary files /dev/null and b/src/images/icon67.png differ diff --git a/src/images/icon68.png b/src/images/icon68.png new file mode 100644 index 0000000..b15f36f Binary files /dev/null and b/src/images/icon68.png differ diff --git a/src/images/svg1.svg b/src/images/svg1.svg new file mode 100644 index 0000000..d5cd380 --- /dev/null +++ b/src/images/svg1.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/pages/act/index.scss b/src/pages/act/index.scss index 1f494fc..770d218 100644 --- a/src/pages/act/index.scss +++ b/src/pages/act/index.scss @@ -186,8 +186,10 @@ page { } } .wrap { + display: flex; + flex-direction: column; + justify-content: space-between; .title { - margin-bottom: 28rpx; font-size: 32rpx; color: rgba(17, 24, 39, 1); line-height: 48rpx; diff --git a/src/pages/actAdd/index.json b/src/pages/actAdd/index.json index bce0229..c72cee9 100644 --- a/src/pages/actAdd/index.json +++ b/src/pages/actAdd/index.json @@ -1,5 +1,7 @@ { "navigationBarTitleText": "创建活动", "navigationStyle": "default", - "usingComponents": {} + "usingComponents": { + "upload-file": "/components/uploadFile/index" + } } diff --git a/src/pages/actAdd/index.scss b/src/pages/actAdd/index.scss index f70eac8..c81123c 100644 --- a/src/pages/actAdd/index.scss +++ b/src/pages/actAdd/index.scss @@ -1,5 +1,11 @@ page { background-color: #f5f7fa; + // 为 uploadFile 组件设置 CSS 变量 + --upload-size: 100%; + --upload-preview-height: 350rpx; // 预览项高度 + --upload-radius: 16rpx; + --upload-bg: rgba(247, 249, 251, 1); + --upload-border: #d1d5db; } .page { @@ -244,14 +250,15 @@ page { } /* ========== 上传 ========== */ -.upload-box { - height: 220rpx; +.upload-inner { + width: 100%; height: 350rpx; border: 2rpx dashed #d1d5db; border-radius: 16rpx; display: flex; align-items: center; justify-content: center; + flex-direction: column; overflow: hidden; background-color: rgba(247, 249, 251, 1); @@ -260,19 +267,14 @@ page { border-color: transparent; } - .upload-inner { - display: flex; - flex-direction: column; - align-items: center; - .upload-camera { - width: 45rpx; - height: 45rpx; - margin-bottom: 12rpx; - } - .upload-text { - font-size: 24rpx; - color: rgba(148, 163, 184, 0.5); - } + .upload-camera { + width: 45rpx; + height: 45rpx; + margin-bottom: 12rpx; + } + .upload-text { + font-size: 24rpx; + color: rgba(148, 163, 184, 0.5); } .upload-img { diff --git a/src/pages/actAdd/index.ts b/src/pages/actAdd/index.ts index b5b8791..08e3dad 100644 --- a/src/pages/actAdd/index.ts +++ b/src/pages/actAdd/index.ts @@ -17,7 +17,7 @@ Page({ ], // 步骤1 基本信息 - coverImage: '', + coverImageList: [] as Array<{ uid: string, url: string, type: string, name: string, size: number }>, title: '', startTime: '', endTime: '', @@ -69,16 +69,13 @@ Page({ }, // ========== 图片上传 ========== - onChooseImage() { - wx.chooseMedia({ - count: 1, - mediaType: ['image'], - sourceType: ['album', 'camera'], - success: (res) => { - const tempFile = res.tempFiles[0]?.tempFilePath - if (tempFile) this.setData({ coverImage: tempFile }) - }, - }) + onCoverUploadSuccess(e: WechatMiniprogram.CustomEvent) { + const { file } = e.detail + this.setData({ coverImageList: [file] }) + }, + + onCoverRemove(_e: WechatMiniprogram.CustomEvent) { + this.setData({ coverImageList: [] }) }, // ========== 输入绑定 ========== diff --git a/src/pages/actAdd/index.wxml b/src/pages/actAdd/index.wxml index 6120811..be2ba70 100644 --- a/src/pages/actAdd/index.wxml +++ b/src/pages/actAdd/index.wxml @@ -26,15 +26,19 @@ 活动头图 * - - - - - - 点击上传 - - - + + + + 点击上传 + + diff --git a/src/pages/actDetail/index.json b/src/pages/actDetail/index.json index 688f367..efc4ed1 100644 --- a/src/pages/actDetail/index.json +++ b/src/pages/actDetail/index.json @@ -4,6 +4,8 @@ "van-count-down": "@vant/weapp/count-down/index", "mp-html": "mp-html", "van-rate": "@vant/weapp/rate/index", - "popup": "/components/popup/index" + "popup": "/components/popup/index", + "van-popup": "@vant/weapp/popup/index", + "uploadFile": "/components/uploadFile/index" } } diff --git a/src/pages/actDetail/index.scss b/src/pages/actDetail/index.scss index 73882de..b0023c8 100644 --- a/src/pages/actDetail/index.scss +++ b/src/pages/actDetail/index.scss @@ -79,7 +79,7 @@ page { } .row-wrap { margin-top: 24rpx; - border-top: 1px solid rgba(136, 142, 152, 0.19); + border-top: 1rpx solid rgba(136, 142, 152, 0.19); .row { margin-top: 34rpx; display: flex; @@ -155,7 +155,7 @@ page { gap: 2rpx; .line-top, .line-bottom { - border-right: 1px dashed rgba(74, 184, 253, 1); + border-right: 1rpx dashed rgba(74, 184, 253, 1); } .line-top { height: 10rpx; @@ -367,7 +367,7 @@ page { } .com { color: rgba(74, 184, 253, 1); - border: 1px solid #4ab8fd; + border: 1rpx solid #4ab8fd; } .btn { color: #fff; @@ -377,12 +377,122 @@ page { } } +.comment-popup { + padding: 32rpx; + .title { + font-size: 36rpx; + color: rgba(17, 24, 39, 1); + font-weight: bold; + } + .rate-wrap { + margin-top: 40rpx; + display: flex; + align-items: center; + justify-content: space-between; + .rate { + display: flex; + align-items: center; + gap: 32rpx; + .num { + font-size: 32rpx; + color: rgba(254, 181, 74, 1); + } + } + .status { + font-size: 28rpx; + color: rgba(254, 181, 74, 1); + } + } + .area-wrap { + padding: 30rpx; + margin-top: 28rpx; + background-color: rgba(247, 248, 250, 1); + border-radius: 24rpx; + .txa { + height: 130rpx; + font-size: 32rpx; + color: rgba(17, 24, 39, 1); + } + .txa-place { + color: rgba(148, 163, 184, 1); + } + .upload-list { + margin-top: 20rpx; + .upload { + width: 108rpx; + height: 108rpx; + background-color: #fff; + border-radius: 16rpx; + display: flex; + align-items: center; + justify-content: center; + .icon { + width: 52rpx; + height: 45rpx; + } + } + } + } + .anonymous { + margin-top: 44rpx; + font-size: 28rpx; + color: #111827; + display: flex; + align-items: center; + .content { + color: #94a3b8; + } + .wx-checkbox-input { + width: 30rpx; + height: 30rpx; + border-radius: 8rpx; + &.wx-checkbox-input-checked { + background: #4ab8fd; + border-color: #4ab8fd; + &::before { + color: #fff; + } + } + } + } + .c-footer { + margin-top: 44rpx; + display: flex; + align-items: center; + gap: 30rpx; + .cancel { + width: 329rpx; + height: 96rpx; + display: flex; + justify-content: center; + align-items: center; + border-radius: 16rpx; + border: 1px solid #4ab8fd; + font-size: 30rpx; + color: #4ab8fd; + box-sizing: border-box; + } + .submit { + width: 329rpx; + height: 96rpx; + display: flex; + justify-content: center; + align-items: center; + border-radius: 16rpx; + font-size: 30rpx; + color: #fff; + box-shadow: 0px 15px 30px -6px rgba(74, 172, 219, 0.4); + background: linear-gradient(90deg, #9ddffd 0%, #4ab8fd 100%); + } + } +} + .slidebar-share { position: fixed; right: 16rpx; - bottom: 200rpx; - width: 180rpx; - height: 180rpx; + bottom: 320rpx; + width: 160rpx; + height: 160rpx; .icon { width: 100%; height: 100%; diff --git a/src/pages/actDetail/index.ts b/src/pages/actDetail/index.ts index 453ecf1..6f0d4c8 100644 --- a/src/pages/actDetail/index.ts +++ b/src/pages/actDetail/index.ts @@ -5,6 +5,8 @@ Page({ popupShow: false, popupType: 'popup1', // 签到成功弹窗 popupParams: {} as any, + + commentShow: false, }, onLoad() {}, handlePopupOk() { @@ -21,6 +23,11 @@ Page({ popupType: 'i', }) }, + onCommentClose() { + this.setData({ + commentShow: false, + }) + }, }) export {} diff --git a/src/pages/actDetail/index.wxml b/src/pages/actDetail/index.wxml index 7f2f0fe..df3161b 100644 --- a/src/pages/actDetail/index.wxml +++ b/src/pages/actDetail/index.wxml @@ -151,6 +151,38 @@ + + + + 评分 + + + + 4.5 + + 非常满意 + + + + + + + + + + + + + 匿名评价 + 你的头像、昵称将隐藏 + + + 取消 + 发布 + + + + () + +Page({ + data: {}, + onLoad() {}, + handleEva() { + wx.navigateTo({ + url: '/pages/agentEva/index', + }) + }, +}) + +export {} diff --git a/src/pages/agent/index.wxml b/src/pages/agent/index.wxml new file mode 100644 index 0000000..ea4a619 --- /dev/null +++ b/src/pages/agent/index.wxml @@ -0,0 +1,112 @@ + + + + + + + + + + + + 推荐智能体 + + 更多 + + + + + + + PPT小助手 + + + + + + 最近使用 + + + + PPT小助 + + + + + 我的收藏 + + + + 文章去 + + + + + + + + + + + + + {{index+1}} + + + + + + 应用包生成助手 + 智能规划课表,轻松掌握学习轻松掌握学习 + 15.6w人使用 + + + + + 评级 + + + + + 收藏 + + 使用 + + + + + + 全部 + 全部 + 全部 + 全部 + + + diff --git a/src/pages/agentEva/index.json b/src/pages/agentEva/index.json new file mode 100644 index 0000000..9b0eec6 --- /dev/null +++ b/src/pages/agentEva/index.json @@ -0,0 +1,7 @@ +{ + "navigationStyle": "default", + "navigationBarTitleText": "智能体", + "usingComponents": { + "van-rate": "@vant/weapp/rate/index" + } +} diff --git a/src/pages/agentEva/index.scss b/src/pages/agentEva/index.scss new file mode 100644 index 0000000..37f9bd2 --- /dev/null +++ b/src/pages/agentEva/index.scss @@ -0,0 +1,219 @@ +page { + background-color: rgba(248, 246, 246, 1); +} +.page { + padding-bottom: 420rpx; + .page-header { + padding: 40rpx; + background-color: #fff; + .logo { + display: block; + margin: 0 auto; + width: 160rpx; + height: 160rpx; + box-shadow: 0px 6.7px 10.04px -6.7px rgba(0, 0, 0, 0.1); + box-shadow: 0px 16.74px 25.11px -5.02px rgba(0, 0, 0, 0.1); + border-radius: 24rpx; + } + .title { + margin-top: 26rpx; + text-align: center; + font-size: 40rpx; + color: rgba(17, 24, 39, 1); + } + .content { + margin-top: 6rpx; + font-size: 28rpx; + color: rgba(107, 114, 128, 1); + text-align: center; + } + .rate-wrap { + margin-top: 76rpx; + display: flex; + align-items: center; + justify-content: space-between; + .num { + font-size: 96rpx; + color: rgba(17, 24, 39, 1); + font-weight: bold; + } + .wrap { + .stat { + margin-top: 5rpx; + font-size: 24rpx; + color: rgba(107, 114, 128, 1); + text-align: right; + } + } + } + } + .list { + margin: 48rpx 30rpx; + .list-title { + font-size: 36rpx; + color: rgba(17, 24, 39, 1); + font-weight: bold; + } + .list-card { + margin: 30rpx 0 0; + padding: 32rpx; + background-color: rgba(255, 255, 255, 1); + border-radius: 32rpx; + .user { + display: flex; + gap: 20rpx; + .avatar { + flex-shrink: 0; + width: 89rpx; + height: 89rpx; + border-radius: 50%; + } + .wrap { + flex: 1; + padding: 7rpx 20rpx 0; + .w-header { + display: flex; + align-items: center; + gap: 13rpx; + .name { + font-size: 32rpx; + color: rgba(17, 24, 39, 1); + font-weight: bold; + } + .idenity { + padding: 6rpx 11rpx; + font-size: 24rpx; + line-height: 1; + border-radius: 7rpx; + color: rgba(74, 184, 253, 1); + background-color: rgba(74, 184, 253, 0.1); + &.teacher { + background-color: rgba(254, 181, 74, 0.1); + color: rgba(254, 181, 74, 1); + } + } + } + .date { + margin-top: 6rpx; + font-size: 24rpx; + color: rgba(107, 114, 128, 1); + } + } + .rate-num { + align-self: start; + padding: 4rpx 14rpx; + font-size: 24rpx; + font-weight: bold; + color: rgba(74, 184, 253, 1); + background: rgba(74, 184, 253, 0.1); + border-radius: 13rpx; + } + } + .content { + margin-top: 22rpx; + font-size: 28rpx; + color: rgba(31, 41, 55, 1); + line-height: 42rpx; + } + .l-footer { + margin-top: 24rpx; + display: flex; + align-items: center; + gap: 24rpx; + .item { + display: flex; + align-items: center; + gap: 12rpx; + .icon, + .icon-active { + width: 36rpx; + height: 36rpx; + } + .icon-active { + display: none; + } + .i-content { + font-size: 28rpx; + color: rgba(156, 163, 175, 1); + } + &.active { + .icon { + display: none; + } + .icon-active { + display: block; + } + .i-content { + color: rgba(254, 181, 74, 1); + } + } + } + } + } + } + .page-footer { + position: fixed; + bottom: 0; + left: 0; + width: 100%; + background: #fff; + border-radius: 40rpx 40rpx 0 0; + box-sizing: border-box; + padding: 32rpx 32rpx calc(env(safe-area-inset-bottom) + 32rpx); + .title { + font-size: 36rpx; + color: rgba(17, 24, 39, 1); + font-weight: bold; + } + .rate-wrap { + margin-top: 19rpx; + display: flex; + align-items: center; + justify-content: space-between; + .rate { + display: flex; + align-items: center; + gap: 36rpx; + .num { + font-size: 32rpx; + color: rgba(254, 181, 74, 1); + } + } + .r-status { + font-size: 28rpx; + color: rgba(254, 181, 74, 1); + } + } + .freetext { + margin-top: 48rpx; + display: flex; + gap: 16rpx; + .txa { + padding: 26rpx 32rpx; + height: 96rpx; + border-radius: 16rpx; + background-color: rgba(247, 248, 250, 1); + box-sizing: border-box; + font-size: 32rpx; + color: rgba(17, 24, 39, 1); + max-height: 400rpx; + } + .txa-place { + color: rgba(148, 163, 184, 1); + } + .btn { + flex-shrink: 0; + width: 130rpx; + height: 96rpx; + font-size: 32rpx; + color: rgba(255, 255, 255, 1); + display: flex; + align-items: center; + justify-content: center; + background: rgba(74, 184, 253, 1); + box-shadow: 0rpx 15rpx 30rpx -6rpx rgba(0, 96, 143, 0.09); + border-radius: 16rpx 16rpx 16rpx 16rpx; + } + } + } +} diff --git a/src/pages/agentEva/index.ts b/src/pages/agentEva/index.ts new file mode 100644 index 0000000..067b6bb --- /dev/null +++ b/src/pages/agentEva/index.ts @@ -0,0 +1,8 @@ +const _app = getApp(); + +Page({ + data: {}, + onLoad() {}, +}); + +export {} diff --git a/src/pages/agentEva/index.wxml b/src/pages/agentEva/index.wxml new file mode 100644 index 0000000..65ed829 --- /dev/null +++ b/src/pages/agentEva/index.wxml @@ -0,0 +1,77 @@ + + + + 应用包生成助手 + 智能应用包构建工具 · 版本 2.4.0 + + 3.5 + + + 共 7 条用户评价 + + + + + 全部评价 + + + + + + 张同学 + 学生 + 教职工 + + 信息工程学院 · 2小时前 + + 3.5 + + + 这个生成助手真的帮了大忙!界面直观,配置流程非常清晰,大大缩短了我们项目部署的时间。建议以后可以增加更多自定义模板。 + + + + + + 34 + + + + 回复 + + + + + + 评分 + + + + 3.5 + + 非常满意 + + + + 发布 + + + diff --git a/src/pages/buses/index.json b/src/pages/buses/index.json new file mode 100644 index 0000000..a674b51 --- /dev/null +++ b/src/pages/buses/index.json @@ -0,0 +1,7 @@ +{ + "navigationBarTitleText": "校园巴士", + "navigationStyle": "default", + "usingComponents": { + "van-icon": "@vant/weapp/icon/index" + } +} diff --git a/src/pages/buses/index.scss b/src/pages/buses/index.scss new file mode 100644 index 0000000..ab087f3 --- /dev/null +++ b/src/pages/buses/index.scss @@ -0,0 +1,314 @@ +page { + background-color: rgba(247, 248, 250, 1); +} +.page { + padding-bottom: calc(120px + env(safe-area-inset-bottom)); + .map { + position: relative; + width: 100%; + height: 584rpx; + .mark { + .mark-container { + padding: 16rpx; + border-radius: 16rpx; + background-color: #fff; + display: flex; + .icon { + width: 36rpx; + height: 36rpx; + } + .wrap { + padding-left: 10rpx; + .title { + font-size: 28rpx; + color: rgba(17, 24, 39, 1); + font-weight: bold; + } + .content { + margin-top: 10rpx; + font-size: 22rpx; + color: rgba(17, 24, 39, 1); + } + } + } + .tril { + margin: -10rpx auto 0; + display: block; + width: 0; + height: 0; + border-style: solid; + border-width: 26rpx 26rpx 0 26rpx; + border-color: #fff transparent transparent transparent; + } + } + .to-center { + position: absolute; + top: 337rpx; + right: 36rpx; + width: 64rpx; + height: 64rpx; + background: #ffffff; + box-shadow: 0rpx 15rpx 30rpx 0rpx rgba(0, 96, 143, 0.09); + border-radius: 12rpx 12rpx 12rpx 12rpx; + display: flex; + align-items: center; + justify-content: center; + .icon { + width: 34rpx; + height: 34rpx; + } + } + } + .banner { + position: relative; + margin: -140rpx 30rpx 0; + padding: 32rpx; + border-radius: 24rpx; + background-color: #fff; + z-index: 1; + .b-header { + display: flex; + align-items: center; + gap: 24rpx; + .icon1 { + flex-shrink: 0; + width: 88rpx; + height: 88rpx; + } + .wrap { + .w-header { + .name { + margin-right: 15rpx; + display: inline-flex; + font-size: 32rpx; + color: rgba(17, 24, 39, 1); + gap: 10rpx; + align-items: center; + flex-wrap: wrap; + .icon2 { + width: 37rpx; + height: 21rpx; + } + } + } + .content { + margin-top: 8rpx; + font-size: 28rpx; + color: rgba(100, 116, 139, 1); + } + } + } + .tip { + margin-top: 32rpx; + padding: 16rpx 24rpx; + border-radius: 16rpx; + display: flex; + align-items: center; + justify-content: space-between; + background-color: rgba(254, 181, 74, 0.1); + gap: 16rpx; + .icon { + flex-shrink: 0; + width: 36rpx; + height: 36rpx; + } + .content { + flex: 1; + font-size: 24rpx; + color: rgba(254, 181, 74, 1); + } + .cross { + font-size: 20rpx; + color: rgba(254, 181, 74, 0.49); + } + } + } + .container { + margin: 24rpx 30rpx 0; + padding: 30rpx; + background-color: rgba(255, 255, 255, 1); + border-radius: 24rpx; + .next { + padding: 32rpx; + background-color: rgba(74, 184, 253, 0.1); + border-radius: 16rpx; + .n-header { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + .title { + font-size: 40rpx; + color: rgba(17, 24, 39, 1); + font-weight: bold; + .high { + display: inline; + color: rgba(74, 184, 253, 1); + } + } + + .tag { + padding: 14rpx 26rpx; + border: 1px solid rgba(74, 184, 253, 1); + border-radius: 40rpx; + font-size: 28rpx; + color: rgba(74, 184, 253, 1); + line-height: 32rpx; + .icon { + width: 23rpx; + height: 26rpx; + } + } + } + .content { + margin-top: 15rpx; + font-size: 28rpx; + color: rgba(100, 116, 139, 1); + } + } + .scroll { + margin: 0 -24rpx; + padding-top: 88rpx; + display: flex; + flex-wrap: nowrap; + overflow-x: auto; + &::-webkit-scrollbar { + display: none; + } + .item { + flex-shrink: 0; + .top { + position: relative; + display: flex; + align-items: center; + justify-content: center; + .badge { + position: absolute; + top: -73rpx; + width: 56rpx; + height: 56rpx; + border-radius: 12rpx; + background: linear-gradient(90deg, #9ddffd 0%, #4ab8fd 100%); + display: none; + align-items: center; + justify-content: center; + box-shadow: 0px 11.2px 28px 0px rgba(0, 96, 143, 0.25); + .icon { + width: 38rpx; + height: 38rpx; + } + &::after { + position: absolute; + bottom: -8rpx; + left: 50%; + transform: translateX(-50%); + display: block; + content: ''; + width: 0; + height: 0; + border-style: solid; + border-width: 10rpx 10rpx 0 10rpx; + border-color: #85ccfc transparent transparent transparent; + } + } + .line-left, + .line-right { + width: 43rpx; + height: 12rpx; + background-color: rgba(247, 248, 250, 1); + } + .center { + position: relative; + z-index: 1; + margin: 0 -14rpx; + border: 2px solid #fff; + width: 40rpx; + height: 40rpx; + box-sizing: border-box; + font-size: 24rpx; + color: rgba(255, 255, 255, 1); + border-radius: 50%; + background-color: rgba(203, 213, 225, 1); + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + } + .sub-center { + position: relative; + z-index: 1; + margin: 8rpx -14rpx; + width: 24rpx; + height: 24rpx; + box-sizing: border-box; + border: 2px solid #fff; + border-radius: 50%; + background-color: rgba(203, 213, 225, 1); + } + } + .name { + margin: 20rpx auto 0; + writing-mode: vertical-lr; + font-size: 28rpx; + color: rgba(71, 85, 105, 1); + letter-spacing: 8rpx; + } + &.active { + .line-left, + .line-right { + background-color: rgba(74, 184, 253, 1); + } + .center { + background-color: rgba(74, 184, 253, 1); + } + .sub-center { + background-color: rgba(74, 184, 253, 1); + } + } + &.last-active { + .badge { + display: flex; + } + .line-right { + background-color: rgba(247, 248, 250, 1); + } + .name { + color: rgba(74, 184, 253, 1); + } + } + &:first-of-type { + .line-left { + opacity: 0; + } + } + &:last-of-type { + .line-right { + opacity: 0; + } + } + } + } + } + .footer { + position: fixed; + bottom: 0; + left: 0; + width: 100%; + box-sizing: border-box; + padding: 30rpx 30rpx calc(env(safe-area-inset-bottom) + 30rpx); + background: rgba(255, 255, 255, 0.8); + box-shadow: 0rpx -4rpx 21rpx 0rpx rgba(0, 0, 0, 0.07); + border-radius: 0rpx 0rpx 0rpx 0rpx; + .btn { + height: 96rpx; + font-size: 32rpx; + color: rgba(255, 255, 255, 1); + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(90deg, #9ddffd 0%, #4ab8fd 100%); + box-shadow: 0rpx 15rpx 30rpx -6rpx rgba(74, 172, 219, 0.4); + border-radius: 16rpx 16rpx 16rpx 16rpx; + } + } +} diff --git a/src/pages/buses/index.ts b/src/pages/buses/index.ts new file mode 100644 index 0000000..8303b08 --- /dev/null +++ b/src/pages/buses/index.ts @@ -0,0 +1,53 @@ +const _app = getApp() +const imagesUrl = _app.globalData.imageUrl + +Page({ + data: { + centerLat: 22.586521, // 纬度 + centerLng: 113.930801, // 经度 + markers: [ + { + id: 1, + latitude: 22.586521, + longitude: 113.930801, + iconPath: `${imagesUrl}/icon64.png`, // 蓝圈白边站点图标 + width: 14, + height: 14, + anchor: { x: 1, y: 1 }, + label: '1111111', + customCallout: { + display: 'ALWAYS', // 永久显示气泡,不用点击 + anchorY: -4, + }, + collision: 'marker', + }, + { + id: 2, + latitude: 22.58893, + longitude: 113.92762, + iconPath: `${imagesUrl}/icon64.png`, // 蓝圈白边站点图标 + anchor: { x: 1, y: 1 }, + width: 14, + height: 14, + customCallout: { + display: 'ALWAYS', // 永久显示气泡,不用点击 + anchorY: -4, + }, + collision: 'marker', + }, + ], + polyline: [ + { + points: [ + { latitude: 22.586521, longitude: 113.930801 }, + { latitude: 22.58893, longitude: 113.92762 }, + ], + color: 'rgba(74, 184, 253, 1)', + width: 6, + }, + ], + }, + onLoad() {}, +}) + +export {} diff --git a/src/pages/buses/index.wxml b/src/pages/buses/index.wxml new file mode 100644 index 0000000..788694a --- /dev/null +++ b/src/pages/buses/index.wxml @@ -0,0 +1,83 @@ + + + + + + + + + + + 留仙洞园区 + 留仙洞园区体育馆·生公寓 距离下一站2分钟 + + + + + + + + + + + + 预计 + 11:55 + 到站 + + + + 发车时刻表 + + + 下一站:官龙山园区报告厅 + + + + + + + + + + + + + + 留仙洞园区体育馆 + + + + + 校园地图 + + diff --git a/src/pages/chat/index.json b/src/pages/chat/index.json new file mode 100644 index 0000000..ed567fe --- /dev/null +++ b/src/pages/chat/index.json @@ -0,0 +1,7 @@ +{ + "navigationBarTitleText": "智能助手", + "usingComponents": { + "van-icon": "@vant/weapp/icon/index" + } +} + diff --git a/src/pages/chat/index.scss b/src/pages/chat/index.scss new file mode 100644 index 0000000..eeaa48f --- /dev/null +++ b/src/pages/chat/index.scss @@ -0,0 +1,160 @@ +page { + background-color: rgba(247, 248, 250, 1); +} +.page-back { + font-size: 32rpx; + color: rgba(0, 0, 0, 0.9); +} + +.chat-page { + width: 100%; + height: 100vh; + box-sizing: border-box; + display: flex; + flex-direction: column; + position: relative; +} + +/* ========== 聊天消息列表 ========== */ +.chat-scroll { + flex: 1; + position: relative; + padding: 160rpx 28rpx 0; + box-sizing: border-box; + box-sizing: border-box; + + .history { + width: 167rpx; + height: 56rpx; + background: linear-gradient(90deg, #9ddffd 0%, #4ab8fd 100%); + font-size: 28rpx; + color: #fff; + display: flex; + align-items: center; + justify-content: center; + border-radius: 54rpx; + } + .first-card { + margin-top: 39rpx; + padding: 32rpx; + font-size: 32rpx; + color: rgba(31, 41, 55, 1); + border-radius: 24rpx; + line-height: 56rpx; + background: linear-gradient(180deg, rgba(241, 249, 255, 0.84) 0%, #ffffff 100%); + border: 1px solid rgba(255, 255, 255, 1); + backdrop-filter: blur(38px); + box-shadow: 0px 15px 30px -4px rgba(0, 96, 143, 0.09); + .high { + display: inline; + color: rgba(74, 184, 253, 1); + } + } + .tip { + margin-top: 42rpx; + font-size: 28rpx; + color: rgba(203, 213, 225, 1); + text-align: center; + } + .chat-messages { + clear: both; + display: flow-root; + .user-message { + clear: both; + float: right; + margin-left: 80rpx; + margin-top: 42rpx; + padding: 24rpx 32rpx; + background: rgba(74, 184, 253, 1); + border-radius: 24rpx; + font-size: 32rpx; + color: #fff; + line-height: 56rpx; + } + .ai-message { + clear: both; + float: left; + margin-right: 80rpx; + margin-top: 42rpx; + padding: 24rpx 32rpx; + font-size: 32rpx; + line-height: 56rpx; + color: rgba(71, 85, 105, 1); + background-color: #fff; + border-radius: 24rpx; + .high { + display: inline; + color: rgba(74, 184, 253, 1); + } + } + } + .scroll-bottom-safe { + clear: both; + height: 223rpx; + } +} + +.chat-input { + position: fixed; + left: 0; + width: calc(100% - 120rpx); + bottom: 83rpx; + margin: 0 30rpx; + padding: 0 18rpx; + height: 96rpx; + border-radius: 95rpx; + background-color: #fff; + box-shadow: 0px 15px 30px 0px rgba(0, 96, 143, 0.09); + .freetext { + padding-left: 26rpx; + height: 100%; + display: flex; + align-items: center; + .input { + flex: 1; + } + .icon { + padding-left: 26rpx; + flex-shrink: 0; + width: 60rpx; + height: 60rpx; + } + } + .voice { + height: 100%; + display: flex; + align-items: center; + .content { + flex: 1; + font-size: 32rpx; + color: rgba(17, 24, 39, 1); + text-align: center; + } + .icon { + padding-left: 26rpx; + flex-shrink: 0; + width: 60rpx; + height: 60rpx; + } + } + .voiceing { + padding: 213rpx 0 0; + position: fixed; + left: 0; + bottom: 0; + width: 100%; + height: 448rpx; + box-sizing: border-box; + .tip { + font-size: 32rpx; + color: #fff; + text-align: center; + } + .ani { + display: block; + margin: 23rpx auto 0; + width: 400rpx; + height: 86rpx; + } + } +} diff --git a/src/pages/chat/index.ts b/src/pages/chat/index.ts new file mode 100644 index 0000000..82e0be0 --- /dev/null +++ b/src/pages/chat/index.ts @@ -0,0 +1,251 @@ +/** + * Chat 聊天页面 + */ + +interface Message { + id: string + role: 'user' | 'ai' + content: string + timestamp: number +} + +Page({ + data: { + /** 消息列表 */ + messages: [] as Message[], + /** 输入文本 */ + inputText: '', + /** 输入模式:text 或 voice */ + inputMode: 'voice' as 'text' | 'voice', + /** 是否正在录音 */ + isRecording: false, + /** 录音提示文本 */ + recordingTip: '松手发送,上移取消', + /** 录音管理器 */ + recorderManager: null as WechatMiniprogram.RecorderManager | null, + /** 滚动到指定消息 */ + scrollToView: '', + /** 页面顶部距离 */ + pageTop: 0, + /** 导航栏背景 */ + background: 'transparent', + }, + + onLoad() { + // 初始化录音管理器 + const recorderManager = wx.getRecorderManager() + recorderManager.onStart(() => { + this.setData({ isRecording: true, recordingTip: '松手发送,上移取消' }) + }) + recorderManager.onStop((res) => { + this.setData({ isRecording: false }) + if (res.duration < 1000) { + wx.showToast({ title: '录音时间太短', icon: 'none' }) + return + } + // 发送语音消息 + this.sendVoiceMessage(res.tempFilePath, res.duration) + }) + recorderManager.onError((err) => { + this.setData({ isRecording: false }) + wx.showToast({ title: '录音失败', icon: 'none' }) + console.error('录音错误:', err) + }) + this.setData({ recorderManager }) + + // 获取系统信息设置页面顶部距离 + const systemInfo = wx.getSystemInfoSync() + this.setData({ pageTop: systemInfo.statusBarHeight + 44 }) + }, + + /** + * 切换到语音模式 + */ + switchToVoice() { + // 如果 input 有内容,不允许切换到语音模式 + if (this.data.inputText.trim()) { + wx.showToast({ title: '请先发送或清空内容', icon: 'none' }) + return + } + this.setData({ inputMode: 'voice' }) + }, + + /** + * 切换到文本模式 + */ + switchToText() { + this.setData({ inputMode: 'text' }) + }, + + /** + * 输入框内容变化 + */ + onInputChange(e: WechatMiniprogram.InputEvent) { + this.setData({ inputText: e.detail.value }) + }, + + /** + * 发送文本消息 + */ + onSendText() { + const { inputText } = this.data + if (!inputText.trim()) return + + // 添加用户消息 + const userMessage: Message = { + id: `user_${Date.now()}`, + role: 'user', + content: inputText.trim(), + timestamp: Date.now(), + } + this.setData({ + messages: [...this.data.messages, userMessage], + inputText: '', + }) + + // 模拟 AI 回复 + this.simulateAIResponse() + }, + + /** + * chat-input 区域长按开始录音 + */ + onChatInputLongPress() { + // 文本模式下有内容时,不允许录音 + if (this.data.inputMode === 'text' && this.data.inputText.trim()) { + wx.showToast({ title: '请先发送或清空内容', icon: 'none' }) + return + } + + const { recorderManager } = this.data + if (!recorderManager) return + + recorderManager.start({ + duration: 60000, + sampleRate: 16000, + numberOfChannels: 1, + encodeBitRate: 48000, + format: 'mp3', + }) + }, + + /** + * chat-input 区域触摸开始 + */ + onChatInputStart() { + // 用于检测长按,不做实际操作 + // 长按逻辑由 bindlongpress 处理 + }, + + /** + * chat-input 区域触摸结束 + */ + onChatInputEnd() { + const { recorderManager, isRecording } = this.data + if (!recorderManager || !isRecording) return + recorderManager.stop() + }, + + /** + * chat-input 区域触摸取消 + */ + onChatInputCancel() { + const { recorderManager, isRecording } = this.data + if (!recorderManager || !isRecording) return + recorderManager.stop() + this.setData({ isRecording: false, recordingTip: '录音已取消' }) + wx.showToast({ title: '录音已取消', icon: 'none' }) + }, + + /** + * voiceing 区域触摸移动(检测手指是否移出) + */ + onVoiceingTouchMove(e: WechatMiniprogram.TouchEvent) { + const { isRecording, recordingTip } = this.data + if (!isRecording) return + + // 获取触摸点位置 + const touch = e.touches[0] + const systemInfo = wx.getSystemInfoSync() + const screenHeight = systemInfo.screenHeight + + // voiceing 区域高度为 448rpx,约 224px + // 当手指 Y 坐标小于 (屏幕高度 - voiceing高度) 时,说明手指移出了区域 + const voiceingHeight = 224 // 448rpx ≈ 224px + const threshold = screenHeight - voiceingHeight + + if (touch.clientY < threshold) { + // 手指移出区域,准备取消 + if (recordingTip !== '上移取消录音') { + this.setData({ recordingTip: '上移取消录音' }) + } + } else { + // 手指在区域内,恢复提示 + if (recordingTip !== '松手发送,上移取消') { + this.setData({ recordingTip: '松手发送,上移取消' }) + } + } + }, + + /** + * voiceing 区域触摸结束 + */ + onVoiceingTouchEnd(e: WechatMiniprogram.TouchEvent) { + const { recorderManager, isRecording, recordingTip } = this.data + if (!recorderManager || !isRecording) return + + // 获取触摸点位置 + const changedTouch = e.changedTouches[0] + const systemInfo = wx.getSystemInfoSync() + const screenHeight = systemInfo.screenHeight + const voiceingHeight = 224 // 448rpx ≈ 224px + const threshold = screenHeight - voiceingHeight + + // 如果手指移出区域,取消录音 + if (changedTouch.clientY < threshold || recordingTip === '上移取消录音') { + recorderManager.stop() + this.setData({ isRecording: false, recordingTip: '录音已取消' }) + wx.showToast({ title: '录音已取消', icon: 'none' }) + } else { + // 手指在区域内,正常结束录音并发送 + recorderManager.stop() + } + }, + + /** + * 发送语音消息 + */ + sendVoiceMessage(filePath: string, duration: number) { + const userMessage: Message = { + id: `user_${Date.now()}`, + role: 'user', + content: `[语音 ${Math.ceil(duration / 1000)}秒]`, + timestamp: Date.now(), + } + this.setData({ + messages: [...this.data.messages, userMessage], + }) + + // 模拟 AI 回复 + this.simulateAIResponse() + }, + + /** + * 模拟 AI 回复 + */ + simulateAIResponse() { + setTimeout(() => { + const aiMessage: Message = { + id: `ai_${Date.now()}`, + role: 'ai', + content: '好的,我已经收到您的消息,正在为您处理中...', + timestamp: Date.now(), + } + this.setData({ + messages: [...this.data.messages, aiMessage], + }) + }, 1000) + }, +}) + +export {} diff --git a/src/pages/chat/index.wxml b/src/pages/chat/index.wxml new file mode 100644 index 0000000..48be2c3 --- /dev/null +++ b/src/pages/chat/index.wxml @@ -0,0 +1,69 @@ + + + + + + 最近对话 + + 你好呀~我是你的活动创建小助手你可以直接告诉我想创建什么活动,比如 + “下周五下午3点在报告厅办一场心理健康讲座” + 我会帮你自动整理活动信息。 + + 特别说明:内容均为ai生成,仅供参考 + + + 请帮我创建一个下周活动,活动主题是新生音乐会,活动地点是学校音乐厅 + + 信息我已经整理好~我还需要再确认一点信息: + 您的活动打算什么时候开始呢?告诉我开始和结束的时间,我来帮您创建~ + + + + + + + + + + + + + + + + 按住说话 + + + + + + + {{recordingTip}} + + + + diff --git a/src/pages/chatHistory/index.json b/src/pages/chatHistory/index.json new file mode 100644 index 0000000..9e257b4 --- /dev/null +++ b/src/pages/chatHistory/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "最近对话", + "navigationStyle": "default", + "usingComponents": {} +} diff --git a/src/pages/chatHistory/index.scss b/src/pages/chatHistory/index.scss new file mode 100644 index 0000000..b9c7d1c --- /dev/null +++ b/src/pages/chatHistory/index.scss @@ -0,0 +1,29 @@ +page { + background-color: rgba(247, 248, 250, 1); +} +.page { + padding: 30rpx; + .card { + padding: 32rpx; + background-color: #fff; + border-radius: 24rpx; + display: flex; + gap: 24rpx; + .wrap { + .title { + font-size: 36rpx; + color: rgba(17, 24, 39, 1); + line-height: 56rpx; + } + .date{ + margin-top: 14rpx; + font-size: 32rpx; + color: rgba(148, 163, 184, 1); + } + } + .status{ + font-size: 32rpx; + color: rgba(203, 213, 225, 1); + } + } +} diff --git a/src/pages/chatHistory/index.ts b/src/pages/chatHistory/index.ts new file mode 100644 index 0000000..067b6bb --- /dev/null +++ b/src/pages/chatHistory/index.ts @@ -0,0 +1,8 @@ +const _app = getApp(); + +Page({ + data: {}, + onLoad() {}, +}); + +export {} diff --git a/src/pages/chatHistory/index.wxml b/src/pages/chatHistory/index.wxml new file mode 100644 index 0000000..d102406 --- /dev/null +++ b/src/pages/chatHistory/index.wxml @@ -0,0 +1,9 @@ + + + + 新生音乐会 + 2026/6/15 + + 草稿 + + diff --git a/src/pages/index/index.wxml b/src/pages/index/index.wxml index f0093e4..dc801b0 100644 --- a/src/pages/index/index.wxml +++ b/src/pages/index/index.wxml @@ -2,6 +2,7 @@ class="page" style="background: url('/images/bg1.png') no-repeat top center/100% 655rpx;padding-top: {{pageTop}}px;" > + 请搜索你想要的内容 diff --git a/src/pages/login/index.scss b/src/pages/login/index.scss index 5c7ea70..d440f29 100644 --- a/src/pages/login/index.scss +++ b/src/pages/login/index.scss @@ -1,3 +1,78 @@ .page { - padding: 20rpx; + padding: 0 30rpx; + .page-title { + padding-top: 70rpx; + font-size: 64rpx; + line-height: 72rpx; + color: rgba(71, 85, 105, 1); + font-weight: bold; + .t1 { + color: rgba(71, 85, 105, 1); + } + .t2 { + color: rgba(74, 184, 253, 1); + } + } + .page-body { + margin-top: 74rpx; + padding: 30rpx; + border-radius: 16rpx; + background-color: #fff; + .tip { + padding: 24rpx; + background-color: rgba(254, 181, 74, 0.1); + border-radius: 16rpx; + display: flex; + gap: 18rpx; + .icon { + flex-shrink: 0; + margin-top: 8rpx; + width: 32rpx; + height: 32rpx; + } + .content { + font-size: 28rpx; + color: rgba(254, 181, 74, 1); + } + } + .form-item { + margin-top: 32rpx; + display: flex; + align-items: center; + gap: 32rpx; + padding: 0 30rpx; + background-color: rgba(248, 250, 253, 1); + border-radius: 16rpx; + .label { + font-size: 32rpx; + color: rgba(17, 24, 39, 1); + &.required::before { + display: inline; + content: '*'; + color: rgba(253, 91, 89, 1); + } + } + .input { + height: 84rpx; + font-size: 32rpx; + color: rgba(71, 85, 105, 1); + } + .input-place { + color: rgba(203, 213, 225, 1); + } + } + .tip2{ + margin-top: 28rpx; + font-size: 28rpx; + color: rgba(148, 163, 184, 1); + line-height: 42rpx; + } + .agreement{ + font-size: 28rpx; + color: rgba(71, 85, 105, 1); + .high{ + color: rgba(74, 184, 253, 1); + } + } + } } diff --git a/src/pages/login/index.wxml b/src/pages/login/index.wxml index 04ed2b5..fe35598 100644 --- a/src/pages/login/index.wxml +++ b/src/pages/login/index.wxml @@ -1,3 +1,30 @@ - - 登录页 + + + 绑定 + 深职大账号 + + + + + + 请确保您输入的密码是正确的,错误次数超过2次SIC将冻结您的账号一小时。超过一定错误次数,学校系统将冻结您的账号。忘记密码请前往 + https://authserver.szpu.edu.cn进行重置 + + + + 账号 + + + + 密码 + + + SIC+仅将您的密码用于单次身份验证,不会保存您的密码 + + 我已阅读并接受《深职大SIC+小程序隐私保护指引》 + + diff --git a/src/pages/my/index.json b/src/pages/my/index.json index a97367d..2d65058 100644 --- a/src/pages/my/index.json +++ b/src/pages/my/index.json @@ -1,3 +1,7 @@ { - "usingComponents": {} + "navigationStyle": "custom", + "navigationBarTitleText": "我的", + "usingComponents": { + "van-icon": "@vant/weapp/icon/index" + } } diff --git a/src/pages/my/index.scss b/src/pages/my/index.scss index 5c7ea70..311b224 100644 --- a/src/pages/my/index.scss +++ b/src/pages/my/index.scss @@ -1,3 +1,96 @@ +page { + background-color: rgba(247, 248, 250, 1); +} .page { - padding: 20rpx; + padding: 0 32rpx; + .user { + padding-top: 50rpx; + display: flex; + gap: 24rpx; + .avatar { + width: 116rpx; + height: 116rpx; + border-radius: 50%; + border: 1px solid #fff; + .a-img { + display: block; + width: 100%; + height: 100%; + } + } + .wrap { + padding-top: 10rpx; + padding-left: 24rpx; + .w-header { + display: flex; + align-items: baseline; + gap: 20rpx; + .name { + font-size: 36rpx; + color: rgba(17, 24, 39, 1); + font-weight: bold; + } + .id { + font-size: 32rpx; + } + } + .content { + margin-top: 16rpx; + font-size: 26rpx; + color: rgba(148, 163, 184, 1); + } + } + } + .kkd { + margin-top: 42rpx; + display: flex; + align-items: center; + justify-content: space-between; + gap: 22rpx; + .k-item { + flex: 1; + background: linear-gradient(180deg, #ebfdff 0%, #ffffff 100%); + border-radius: 24rpx 24rpx 24rpx 24rpx; + border: 2rpx solid #ffffff; + .wrap { + padding: 32rpx; + .title { + font-size: 32rpx; + color: rgba(17, 24, 39, 1); + font-weight: bold; + } + .content { + margin-top: 16rpx; + font-size: 28rpx; + color: rgba(148, 163, 184, 1); + } + } + } + } + .list { + margin-top: 30rpx; + background-color: #fff; + border-radius: 24rpx; + padding: 0 32rpx; + .list-item { + padding: 32rpx 0; + display: flex; + align-items: center; + justify-content: space-between; + gap: 18rpx; + font-size: 28rpx; + color: rgba(203, 213, 225, 1); + border-bottom: 1px solid rgba(203, 213, 225, 0.21); + .icon { + flex-shrink: 0; + width: 42rpx; + height: 42rpx; + } + .name { + flex: 1; + font-size: 28rpx; + color: rgba(17, 24, 39, 1); + } + } + } } diff --git a/src/pages/my/index.ts b/src/pages/my/index.ts index 9e60acc..1ff77c9 100644 --- a/src/pages/my/index.ts +++ b/src/pages/my/index.ts @@ -12,6 +12,21 @@ Page({ userInfo: app.globalData.userInfo, }) }, + handleMyAct() { + wx.navigateTo({ + url: '/pages/myAct/index', + }) + }, + handleMyAgent() { + wx.navigateTo({ + url: '/pages/myAgent/index', + }) + }, + handleMyCommet() { + wx.navigateTo({ + url: '/pages/myComment/index', + }) + }, }) export {} diff --git a/src/pages/my/index.wxml b/src/pages/my/index.wxml index fa18c8b..43bd32f 100644 --- a/src/pages/my/index.wxml +++ b/src/pages/my/index.wxml @@ -1,3 +1,48 @@ - - 我的 + + + + + + + + 奇妙的双子座 + 20989182 + + 计算机科学学院软件工程3班 + + + + + + 我的活动 + 已参与10个活动 + + + + + 我的智能体 + 已启用20个智能体 + + + + + + + 我的评论 + + + + + 我的收藏 + + + + + 我的评论 + + + diff --git a/src/pages/myAct/index.json b/src/pages/myAct/index.json new file mode 100644 index 0000000..062154f --- /dev/null +++ b/src/pages/myAct/index.json @@ -0,0 +1,9 @@ +{ + "navigationBarTitleText": "我的活动", + "navigationStyle": "default", + "usingComponents": { + "van-tab": "@vant/weapp/tab/index", + "van-tabs": "@vant/weapp/tabs/index", + "van-icon": "@vant/weapp/icon/index" + } +} diff --git a/src/pages/myAct/index.scss b/src/pages/myAct/index.scss new file mode 100644 index 0000000..330121e --- /dev/null +++ b/src/pages/myAct/index.scss @@ -0,0 +1,141 @@ +page { + background-color: rgba(247, 248, 250, 1); +} + +.page { + .tabs { + .van-tabs__line { + width: 42rpx !important; + } + .van-tab--active { + --tab-font-size: 32rpx; + } + } + .page0 { + padding: 43rpx 30rpx; + .card { + margin-bottom: 24rpx; + padding: 24rpx; + background-color: #fff; + border-radius: 24rpx; + .c-body { + display: flex; + gap: 24rpx; + padding-bottom: 24rpx; + border-bottom: 1px solid rgba(247, 248, 250, 1); + &:last-of-type { + border: none; + } + .photo { + flex-shrink: 0; + position: relative; + width: 262rpx; + height: 196rpx; + border-radius: 16rpx; + overflow: hidden; + .status { + position: absolute; + top: 0; + left: 0; + padding: 8rpx 16rpx; + font-size: 22rpx; + border-radius: 16rpx 0rpx 16rpx 0rpx; + color: rgba(255, 255, 255, 1); + &.status1 { + background: #feb54a; + } + &.status2 { + background: rgba(74, 184, 253, 1); + } + &.status3 { + background: rgba(111, 220, 174, 1); + } + &.status4 { + background: rgba(203, 213, 225, 1); + } + } + .p-img { + border-radius: 16rpx; + display: block; + width: 100%; + height: 100%; + } + .user { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + box-sizing: border-box; + text-align: center; + line-height: 44rpx; + font-size: 24rpx; + color: rgba(255, 255, 255, 1); + background-color: rgba(0, 0, 0, 0.36); + backdrop-filter: blur(8rpx); + } + } + .wrap { + display: flex; + flex-direction: column; + justify-content: space-between; + .title { + font-size: 32rpx; + color: rgba(17, 24, 39, 1); + line-height: 48rpx; + font-weight: bold; + height: 96rpx; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + } + .date, + .site { + margin-top: 12rpx; + font-size: 28rpx; + color: rgba(100, 116, 139, 1); + display: flex; + align-items: center; + gap: 8rpx; + .icon { + width: 22rpx; + height: 22rpx; + } + } + } + } + .c-footer { + padding-top: 24rpx; + display: flex; + align-items: center; + justify-content: flex-end; + flex-wrap: wrap; + gap: 16rpx; + .stat { + flex-grow: 1; + flex-shrink: 0; + font-size: 28rpx; + color: rgba(148, 163, 184, 1); + } + .btn1 { + padding: 12rpx 32rpx; + border: 1px solid rgba(74, 184, 253, 1); + border-radius: 50rpx; + font-size: 28rpx; + line-height: 32rpx; + color: rgba(74, 184, 253, 1); + } + .btn2 { + padding: 12rpx 32rpx; + border: 1px solid rgba(74, 184, 253, 1); + border-radius: 50rpx; + font-size: 28rpx; + line-height: 32rpx; + color: #fff; + background-color: rgba(74, 184, 253, 1); + } + } + } + } +} diff --git a/src/pages/myAct/index.ts b/src/pages/myAct/index.ts new file mode 100644 index 0000000..067b6bb --- /dev/null +++ b/src/pages/myAct/index.ts @@ -0,0 +1,8 @@ +const _app = getApp(); + +Page({ + data: {}, + onLoad() {}, +}); + +export {} diff --git a/src/pages/myAct/index.wxml b/src/pages/myAct/index.wxml new file mode 100644 index 0000000..843d305 --- /dev/null +++ b/src/pages/myAct/index.wxml @@ -0,0 +1,49 @@ + + + + + + + + 进行中 + 已发布 + 报名中 + 草稿 + 已取消 + 已结束 + + 128人已报名 + + + 深职大第十五届校园歌手大赛 + + + 2026.04.01-2026.05.30 + + + + 留仙洞校区音乐厅 + + + + + 86人已报名 + 删除 + + + + 编辑 + + + + + 内容 2 + + diff --git a/src/pages/myAgent/index.json b/src/pages/myAgent/index.json new file mode 100644 index 0000000..418827f --- /dev/null +++ b/src/pages/myAgent/index.json @@ -0,0 +1,9 @@ +{ + "navigationBarTitleText": "我的智能体", + "navigationStyle": "default", + "usingComponents": { + "van-tab": "@vant/weapp/tab/index", + "van-tabs": "@vant/weapp/tabs/index", + "van-icon": "@vant/weapp/icon/index" + } +} diff --git a/src/pages/myAgent/index.scss b/src/pages/myAgent/index.scss new file mode 100644 index 0000000..13d7a88 --- /dev/null +++ b/src/pages/myAgent/index.scss @@ -0,0 +1,95 @@ +page { + background-color: rgba(247, 248, 250, 1); +} + +.page { + .tabs { + .van-tabs__line { + width: 42rpx !important; + } + .van-tab--active { + --tab-font-size: 32rpx; + } + } + .page0 { + padding: 43rpx 30rpx; + .card { + margin-bottom: 24rpx; + padding: 24rpx; + background-color: #fff; + border-radius: 24rpx; + display: flex; + gap: 24rpx; + padding-bottom: 24rpx; + border-bottom: 1px solid rgba(247, 248, 250, 1); + &:last-of-type { + border: none; + } + .photo { + flex-shrink: 0; + position: relative; + width: 158rpx; + height: 158rpx; + border-radius: 16rpx; + overflow: hidden; + .p-img { + border-radius: 16rpx; + display: block; + width: 100%; + height: 100%; + } + } + .wrap { + flex: 1; + .title { + font-size: 32rpx; + color: rgba(17, 24, 39, 1); + line-height: 48rpx; + font-weight: bold; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + } + .content { + margin-top: 12rpx; + font-size: 28rpx; + color: rgba(148, 163, 184, 1); + line-height: 36rpx; + } + .w-footer { + padding-top: 20rpx; + display: flex; + align-items: center; + justify-content: flex-end; + flex-wrap: wrap; + gap: 16rpx; + .stat { + flex-grow: 1; + flex-shrink: 0; + font-size: 28rpx; + color: rgba(148, 163, 184, 0.5); + } + .btn1 { + padding: 8rpx 32rpx; + border: 1px solid rgba(74, 184, 253, 1); + border-radius: 50rpx; + font-size: 28rpx; + line-height: 32rpx; + color: rgba(74, 184, 253, 1); + } + .btn2 { + padding: 8rpx 32rpx; + border: 1px solid rgba(74, 184, 253, 1); + border-radius: 50rpx; + font-size: 28rpx; + line-height: 32rpx; + color: #fff; + background-color: rgba(74, 184, 253, 1); + } + } + } + } + } +} diff --git a/src/pages/myAgent/index.ts b/src/pages/myAgent/index.ts new file mode 100644 index 0000000..067b6bb --- /dev/null +++ b/src/pages/myAgent/index.ts @@ -0,0 +1,8 @@ +const _app = getApp(); + +Page({ + data: {}, + onLoad() {}, +}); + +export {} diff --git a/src/pages/myAgent/index.wxml b/src/pages/myAgent/index.wxml new file mode 100644 index 0000000..38dfafe --- /dev/null +++ b/src/pages/myAgent/index.wxml @@ -0,0 +1,31 @@ + + + + + + + + + + 深职大第十五届校园歌手大赛 +
智能规划课表,轻松掌握学习节奏
+ + 86人已报名 + 取消收藏 + 使用 + + + + + + 内容 2 + 内容 2 + + diff --git a/src/pages/myComment/index.json b/src/pages/myComment/index.json new file mode 100644 index 0000000..fd56525 --- /dev/null +++ b/src/pages/myComment/index.json @@ -0,0 +1,10 @@ +{ + "navigationBarTitleText": "我的评论", + "navigationStyle": "default", + "usingComponents": { + "van-tab": "@vant/weapp/tab/index", + "van-tabs": "@vant/weapp/tabs/index", + "van-icon": "@vant/weapp/icon/index", + "van-rate": "@vant/weapp/rate/index" + } +} diff --git a/src/pages/myComment/index.scss b/src/pages/myComment/index.scss new file mode 100644 index 0000000..33762b1 --- /dev/null +++ b/src/pages/myComment/index.scss @@ -0,0 +1,167 @@ +page { + background-color: rgba(247, 248, 250, 1); +} + +.page { + .tabs { + .van-tabs__line { + width: 42rpx !important; + } + .van-tab--active { + --tab-font-size: 32rpx; + } + } + .page0 { + padding: 43rpx 30rpx; + .card { + padding: 32rpx; + background-color: #fff; + border-radius: 24rpx; + .c-header { + display: flex; + align-items: center; + justify-content: space-between; + .status { + padding: 8rpx 16rpx; + font-size: 28rpx; + line-height: 32rpx; + border-radius: 8rpx; + &.status1 { + color: rgba(74, 184, 253, 1); + background-color: rgba(74, 184, 253, 0.1); + } + &.status2 { + color: rgba(111, 220, 174, 1); + background-color: rgba(111, 220, 174, 0.1); + } + &.status3 { + color: rgba(253, 91, 89, 1); + background-color: rgba(253, 91, 89, 0.1); + } + } + .wrap { + display: flex; + align-items: center; + gap: 24rpx; + .name { + font-size: 28rpx; + color: rgba(148, 163, 184, 1); + } + .options { + .icon { + width: 32rpx; + height: 32rpx; + } + } + } + } + .station { + margin-top: 28rpx; + display: flex; + align-items: center; + justify-content: space-between; + .date { + font-size: 28rpx; + color: rgba(148, 163, 184, 1); + } + .rate { + display: flex; + align-items: center; + gap: 8rpx; + .num { + font-size: 28rpx; + color: rgba(254, 181, 74, 1); + } + } + } + .content { + margin-top: 32rpx; + font-size: 28rpx; + color: rgba(17, 24, 39, 1); + line-height: 46rpx; + } + .photo-wrap { + margin-top: 24rpx; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16rpx; + flex-wrap: wrap; + .photo { + aspect-ratio: 1 / 1; + .p-img { + display: block; + width: 100%; + height: 100%; + border-radius: 16rpx; + } + } + } + .act-card { + margin-top: 24rpx; + padding: 16rpx; + border-radius: 16rpx; + background-color: rgba(249, 250, 251, 1); + display: flex; + align-items: center; + gap: 24rpx; + .ac-photo { + flex-shrink: 0; + border-radius: 8rpx; + width: 80rpx; + height: 80rpx; + } + .wrap { + padding-top: 5rpx; + .title { + font-size: 32rpx; + color: rgba(17, 24, 39, 1); + font-weight: bold; + } + .content { + margin-top: 8rpx; + font-size: 24rpx; + color: rgba(148, 163, 184, 1); + } + } + } + .tip-card { + margin-top: 24rpx; + border-radius: 16rpx; + background: rgba(74, 184, 253, 0.05); + padding: 20rpx 16rpx; + display: flex; + gap: 16rpx; + .icon { + margin-top: 5rpx; + flex-shrink: 0; + width: 22rpx; + height: 22rpx; + } + .t-content { + font-size: 28rpx; + line-height: 32rpx; + color: rgba(74, 184, 253, 1); + } + } + .reject-card { + margin-top: 24rpx; + border-radius: 16rpx; + background: rgba(253, 91, 89, 0.1); + padding: 20rpx 16rpx; + display: flex; + gap: 16rpx; + .icon { + margin-top: 5rpx; + flex-shrink: 0; + width: 22rpx; + height: 22rpx; + } + .t-content { + font-size: 28rpx; + line-height: 32rpx; + color: rgba(253, 91, 89, 1); + } + } + } + } +} diff --git a/src/pages/myComment/index.ts b/src/pages/myComment/index.ts new file mode 100644 index 0000000..067b6bb --- /dev/null +++ b/src/pages/myComment/index.ts @@ -0,0 +1,8 @@ +const _app = getApp(); + +Page({ + data: {}, + onLoad() {}, +}); + +export {} diff --git a/src/pages/myComment/index.wxml b/src/pages/myComment/index.wxml new file mode 100644 index 0000000..114f043 --- /dev/null +++ b/src/pages/myComment/index.wxml @@ -0,0 +1,58 @@ + + + + + + + 待审核 + + 匿名 + + + + + + + 2026-01-01 13:00:09 + + + 4.5 + + + + 本次活动组织有序、流程顺畅,现场氛围良好。活 动内容丰富、安排合理,服务贴心到位,整体体验 良好。 + + + + + + + + + + 深职大第十五届校园歌手大赛 + 2026年5月30日 19:00 + + + + + 内容已提交,正在审核中,预计1-2个工作日内完成审核。 + + + + 驳回原因:内容中包含不当言论,请修改后重新提交。 + + + + + 内容 2 + 内容 2 + +