Commit 844b9efe authored by fengyu's avatar fengyu

V1.0.0

parents
# 环境标识
VITE_APP_ENV = 'development'
# URL路径
VITE_APP_BASE_URL = '/practice-jiaxiu-mobile/'
# API地址
VITE_APP_BASE_API = 'https://info.gyntv.com.cn/back/ty-visit/ty-visit'
#组织标识
VITE_APP_VISITOR_CODE = 'practice-mediaCatalog'
#文件路径
VITE_APP_FILE_URL = '/download'
#kkfile地址
VITE_APP_PREVIEW_URL = 'http://jiaxiu.gyntv.com.cn:8012/onlinePreview?url='
#打包文件名称
VITE_APP_OUTDIR_FILENAME = 'practice-jiaxiu-mobile-html'
#systemCode
VITE_APP_SYSTEM_CODE = 'ty-centre'
#爽贵阳appid
VITE_APP_SGY_APPID = '685669b8b471aef47e2968a581ca03cd'
\ No newline at end of file
# 环境标识
VITE_APP_ENV = 'production'
# URL路径
VITE_APP_BASE_URL = '/practice-jiaxiu-mobile/'
# API地址
VITE_APP_BASE_API = 'https://info.gyntv.com.cn/back/ty-visit/ty-visit'
#组织标识
VITE_APP_VISITOR_CODE = 'practice-mediaCatalog'
#文件路径
VITE_APP_FILE_URL = 'https://info.gyntv.com.cn//download'
#kkfile地址
VITE_APP_PREVIEW_URL = 'http://jiaxiu.gyntv.com.cn:8012/onlinePreview?url='
#打包文件名称
VITE_APP_OUTDIR_FILENAME = 'practice-jiaxiu-mobile-html'
#systemCode
VITE_APP_SYSTEM_CODE = 'ty-centre'
#爽贵阳appid
VITE_APP_SGY_APPID = '685669b8b471aef47e2968a581ca03cd'
\ No newline at end of file
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
build
*.local
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.rexma
stages:
- install
- deploy
cache:
key: jiaxiuvisit
paths:
- node_modules/
job_install:
stage: install
image: fyout.docker.harbor/library/arm-node:v17.3.0
script:
- npm cache clean --force
- npm config set registry http://registry.npm.taobao.org/
- npm install # 安装项目依赖
- npm install sass --save-dev
- npm install vite-plugin-sass --save-dev
- npm run build # 执行构建命令,生成静态文件1
artifacts:
paths:
- practice-tcsbm-html/ # 假设你的构建输出在 "dist/" 目录下2
tags:
- docker-node
only:
- master
when: manual
deploy:
stage: deploy
script:
- echo "test with tag build,gitlab-runner免密3"
- echo $USER
- su - gitlab-runner
- ssh root@172.16.7.4 mkdir -p /data/fengyu/prod/nginx/html/jiaxiu-visit-h5
- scp -r ./practice-tcsbm-html/** root@172.16.7.4:/data/fengyu/prod/nginx/html/jiaxiu-visit-h5
tags:
- shell
only:
- master
when: manual
\ No newline at end of file
stages:
- install
- deploy
cache:
key: jiaxiuvisit
paths:
- node_modules/
job_install:
stage: install
image: fyout.docker.harbor/library/arm-node:v17.3.0
script:
- npm cache clean --force
- npm config set registry http://registry.npm.taobao.org/
- npm install # 安装项目依赖
- npm install sass --save-dev
- npm install vite-plugin-sass --save-dev
- npm run build # 执行构建命令,生成静态文件1111
artifacts:
paths:
- practice-tcsbm-html/ # 假设你的构建输出在 "dist/" 目录下2
tags:
- docker-node
only:
- master
when: manual
deploy:
stage: deploy
script:
- echo "test with tag build,gitlab-runner免密3"
- echo $USER
- su - gitlab-runner
- ssh root@172.16.7.31 mkdir -p /data/k8snacos/prod/nginx/html/jiaxiu-visit-h5
- scp -r ./practice-tcsbm-html/** root@172.16.7.31:/data/k8snacos/prod/nginx/html/jiaxiu-visit-h5
tags:
- shell
only:
- master
when: manual
\ No newline at end of file
stages:
- install
- deploy
cache:
key: jiaxiuvisit
paths:
- node_modules/
job_install:
stage: install
script:
# - npm cache clean --force
- npm config set registry http://registry.npm.taobao.org/
# - npm install # 安装项目依赖
# - npm install sass --save-dev
# - npm install vite-plugin-sass --save-dev
- npm run build # 执行构建命令,生成静态文件1
artifacts:
paths:
- practice-tcsbm-html/ # 假设你的构建输出在 "dist/" 目录下2
tags:
- docker-node
only:
- master
when: manual
deploy:
stage: deploy
script:
- echo "test with tag build,gitlab-runner免密3"
- echo $USER
- su - gitlab-runner
- ssh root@172.16.6.21 mkdir -p /data/fengyu/prod/nginx/html/jiaxiu-visit-h5
- scp -r ./practice-tcsbm-html/** root@172.16.6.21:/data/fengyu/prod/nginx/html/jiaxiu-visit-h5
tags:
- shell
only:
- master
when: manual
\ No newline at end of file
stages:
- install
- deploy
cache:
key: jiaxiuvisit
paths:
- node_modules/
job_install:
stage: install
script:
- npm cache clean --force
- npm config set registry http://registry.npm.taobao.org/
- npm install # 安装项目依赖
- npm install sass --save-dev
- npm install vite-plugin-sass --save-dev
- npm run build # 执行构建命令,生成静态文件1
artifacts:
paths:
- practice-tcsbm-html/ # 假设你的构建输出在 "dist/" 目录下2
tags:
- docker-node
only:
- master
when: manual
deploy:
stage: deploy
script:
- echo "test with tag build,gitlab-runner免密3"
- echo $USER
- su - gitlab-runner
- ssh root@192.168.0.60 mkdir -p /data/fengyu/prod/nginx/html/jiaxiu-visit-h5
- scp -r ./practice-tcsbm-html/** root@192.168.0.60:/data/fengyu/prod/nginx/html/jiaxiu-visit-h5
tags:
- shell
only:
- master
when: manual
\ No newline at end of file
stages:
- install
- deploy
cache:
key: jiaxiuvisit
paths:
- node_modules/
job_install:
stage: install
script:
- npm cache clean --force
- npm config set registry http://registry.npm.taobao.org/
- npm install # 安装项目依赖
- npm install sass --save-dev
- npm install vite-plugin-sass --save-dev
- npm run build # 执行构建命令,生成静态文件1
artifacts:
paths:
- practice-tcsbm-html/ # 假设你的构建输出在 "dist/" 目录下2
tags:
- docker-node
only:
- master
when: manual
deploy:
stage: deploy
script:
- echo "test with tag build,gitlab-runner免密3"
- echo $USER
- su - gitlab-runner
- ssh root@192.168.0.50 mkdir -p /data/fengyu/prod/nginx/html/jiaxiu-visit-h5
- scp -r ./practice-tcsbm-html/** root@192.168.0.50:/data/fengyu/prod/nginx/html/jiaxiu-visit-h5
tags:
- shell
only:
- master
when: manual
\ No newline at end of file
// .npmrc
public-hoist-pattern[]=@vue*
// or
// shamefully-hoist = true
# 小程序平台模板 vue3
## 安装依赖
```
yarn install
```
### 本地服务
```
yarn start
```
### 小程序平台打包
```
yarn build
```
## 默认安装的 UI 库
[wot-design-uni](https://wot-design-uni.netlify.app/component/button.html)
### 自定义爽贵阳条件编译平台 H5-SGY
#### 1.配置扩展平台
```
//package.json
"uni-app": {
"scripts": {
"h5-sgy": {
"title": "爽贵阳平台",
"env": {
"UNI_PLATFORM": "h5"
},
"define": {
"H5-SGY": true
}
}
}
}
```
#### 2.修改启动命令
```
//package.json
"scripts": {
"start": "rexma start-rexma",
"build": "rexma build-rexma",
"start:platform": "rexma start",
"build:platform": "rexma build"
},
//package.json
"scripts": {
"start": "uni -p h5-sgy",
"build": "uni build -p h5-sgy",
"start:platform": "rexma start",
"build:platform": "rexma build"
}
```
#### 3.使用方式参考 main.js
```
// #ifdef H5
console.log('当前基准平台:H5')
// #endif
// #ifdef H5-SGY
console.log('当前扩展平台:H5-SGY')
// #endif
```
##### 注意:H5-SGY 条件编译是基于 H5 条件编译进行扩展,H5-SGY 条件编译和 H5 条件编译会同时生效。若需要仅 H5 条件编译生效,请指定运行或打包平台为 H5。
/* eslint-disable */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
VanButton: typeof import('vant/es')['Button']
VanCalendar: typeof import('vant/es')['Calendar']
VanCascader: typeof import('vant/es')['Cascader']
VanCol: typeof import('vant/es')['Col']
VanEmpty: typeof import('vant/es')['Empty']
VanField: typeof import('vant/es')['Field']
VanForm: typeof import('vant/es')['Form']
VanIcon: typeof import('vant/es')['Icon']
VanList: typeof import('vant/es')['List']
VanNavBar: typeof import('vant/es')['NavBar']
VanPopup: typeof import('vant/es')['Popup']
VanProgress: typeof import('vant/es')['Progress']
VanPullRefresh: typeof import('vant/es')['PullRefresh']
VanRow: typeof import('vant/es')['Row']
VanSearch: typeof import('vant/es')['Search']
VanStep: typeof import('vant/es')['Step']
VanSteps: typeof import('vant/es')['Steps']
VanSticky: typeof import('vant/es')['Sticky']
VanTab: typeof import('vant/es')['Tab']
VanTabs: typeof import('vant/es')['Tabs']
VanTextEllipsis: typeof import('vant/es')['TextEllipsis']
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
<title>甲秀图库</title>
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
\ No newline at end of file
{
"name": "rexma-temp-vue3",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"start": "rexma start-rexma --custom h5-sgy",
"build": "rexma build-rexma --custom h5-sgy",
"start:platform": "rexma start",
"build:platform": "rexma build"
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-4000720240327002",
"@dcloudio/uni-app-plus": "3.0.0-4000720240327002",
"@dcloudio/uni-components": "3.0.0-4000720240327002",
"@dcloudio/uni-h5": "3.0.0-4000720240327002",
"@dcloudio/uni-mp-alipay": "3.0.0-4000720240327002",
"@dcloudio/uni-mp-baidu": "3.0.0-4000720240327002",
"@dcloudio/uni-mp-jd": "3.0.0-4000720240327002",
"@dcloudio/uni-mp-kuaishou": "3.0.0-4000720240327002",
"@dcloudio/uni-mp-lark": "3.0.0-4000720240327002",
"@dcloudio/uni-mp-qq": "3.0.0-4000720240327002",
"@dcloudio/uni-mp-toutiao": "3.0.0-4000720240327002",
"@dcloudio/uni-mp-weixin": "3.0.0-4000720240327002",
"@dcloudio/uni-mp-xhs": "3.0.0-4000720240327002",
"@dcloudio/uni-quickapp-webview": "3.0.0-4000720240327002",
"@vant/area-data": "^1.5.1",
"axios": "^1.7.2",
"dayjs": "^1.11.11",
"pinia": "2.0.36",
"pinia-plugin-persist": "^1.0.0",
"vant": "^4.9.1",
"vconsole": "^3.15.1",
"vue": "^3.3.11",
"vue-i18n": "^9.1.9",
"vue-router": "4",
"wot-design-uni": "^1.2.26",
"xinhua-sdk": "^1.6.0"
},
"devDependencies": {
"@dcloudio/types": "^3.3.2",
"@dcloudio/uni-automator": "3.0.0-4000720240327002",
"@dcloudio/uni-cli-shared": "3.0.0-4000720240327002",
"@dcloudio/uni-stacktracey": "3.0.0-4000720240327002",
"@dcloudio/vite-plugin-uni": "3.0.0-4000720240327002",
"@types/node": "^20.14.5",
"@vant/auto-import-resolver": "^1.2.1",
"@vitejs/plugin-legacy": "4.1.1",
"@vue/runtime-core": "^3.3.11",
"@vue/tsconfig": "^0.1.3",
"add": "^2.0.6",
"rexma-cli": "^1.9.3",
"sass": "^1.77.6",
"typescript": "^4.9.4",
"uni-mini-router": "^0.1.6",
"uni-parse-pages": "^0.0.1",
"unplugin-auto-import": "^0.17.6",
"unplugin-vue-components": "^0.27.2",
"vite": "4.3.5",
"vue-tsc": "^1.0.24"
},
"uni-app": {
"scripts": {
"h5-sgy": {
"title": "爽贵阳平台",
"env": {
"UNI_PLATFORM": "h5",
"REXMA": true
},
"define": {
"H5-SGY": true
}
}
}
}
}
\ No newline at end of file
<script setup lang="ts">
onLaunch(() => {
console.log('App Launch');
});
onShow(() => {
console.log('App Show');
});
onHide(() => {
console.log('App Hide');
});
</script>
<style></style>
import request from '@/utils/request'
/**
* @description 账号密码登录
* @param {any} data 账号密码登录
* @returns
*/
export const loginByPassword = (data) => {
return request({
url: '/login',
method: 'post',
data: data
})
}
/**
* @description 爽贵阳快捷登录
* @param {any} data 爽贵阳快捷登录
* @returns
*/
export const loginBySgy = (data) => {
return request({
url: '/sgylogin',
method: 'post',
data: data
})
}
\ No newline at end of file
import request from '@/utils/request'
/**
* @description 组织列表查询
* @param {Any} data 标签对象
* @returns
*/
export const selectOrganizeList = (data) => {
return request({
url: '/app/appMediaSelect/selectAppOrganizeList',
method: 'post',
data: data
})
}
/**
* @description 专区列表查询
* @param {Any} page 分页对象
* @param {Any} data 图组对象
* @returns
*/
export const selectPrefecturetList = (page,data) => {
return request({
url: `/app/appMediaSelect/selectAppMediaByOrganIdPage/${page.pageNum}/${page.pageSize}`,
method: 'post',
data: data
})
}
\ No newline at end of file
import request from '@/utils/request'
/**
* @description 上传图片
* @param {File} file File对象
* @param {Function} handleUploadProgress 上传进度回调
* @returns
*/
export const uploadFile = (file, handleUploadProgress) => {
const formData = new FormData()
formData.append("file", file)
formData.append("systemCode", import.meta.env.VITE_APP_SYSTEM_CODE)
return request({
url: '/html/htmlMediaInsert/htmlSingleUpload',
method: 'post',
data: formData,
onUploadProgress: handleUploadProgress,
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
/**
* @description 查询类型数据
* @returns
*/
export const selectTypeList = () => {
const data = {
materialType: 3
}
return request({
url: '/html/htmlMediaSelect/selectHtmlClassifyList',
method: 'post',
data: data
})
}
/**
* @description 查询来源数据
* @param {Number} sourceType 1.来源类型 2.来源渠道
* @returns
*/
export const selectSourceList = (sourceType) => {
const data = { sourceType }
return request({
url: '/html/htmlMediaSelect/selectHtmlSourceList',
method: 'post',
data: data
})
}
/**
* @description 查询标签数据
* @returns
*/
export const selectTagsList = () => {
const data = {}
return request({
url: `/html/htmlMediaSelect/treeHtmlTagData/${import.meta.env.VITE_APP_SYSTEM_CODE}`,
method: 'post',
data: data
})
}
/**
* @description 精细查询图组列表
* @param {Any} page 分页对象
* @param {Any} data 图组对象
* @returns
*/
export const selectFinePictureList = (page, data) => {
return request({
url: `/app/appMediaSelect/selectAppFineCatalogList/${page.pageNum}/${page.pageSize}`,
method: 'post',
data: data
})
}
/**
* @description 广泛查询图组列表
* @param {Any} page 分页对象
* @param {Any} data 图组对象
* @returns
*/
export const selectCoarsePictureList = (page, data) => {
return request({
url: `/app/appMediaSelect/selectAppCatalogList/${page.pageNum}/${page.pageSize}`,
method: 'post',
data: data
})
}
/**
* @description 我的图组列表
* @param {Any} page 分页对象
* @param {Any} data 图组对象
* @returns
*/
export const selectMinePictureList = (page, data) => {
return request({
url: `/app/appMediaSelect/selectAppMaterialRetrievalList/${page.pageNum}/${page.pageSize}`,
method: 'post',
data: data
})
}
/**
* @description 我的收藏列表
* @param {Any} page 分页对象
* @param {Any} data 图组对象
* @returns
*/
export const selectCollectPictureList = (page, data) => {
return request({
url: `/app/appMediaSelect/selectAppPersonMaterialList/${page.pageNum}/${page.pageSize}`,
method: 'post',
data: data
})
}
/**
* @description 发布图组
* @param {Any} data 图组对象
* @returns
*/
export const publishPicture = ( data) => {
return request({
url: '/app/appMediaUpdate/updateAppCollect',
method: 'post',
data: data
})
}
/**
* @description 新增图组
* @param {Any} data 图组对象
* @returns
*/
export const insertPicture = (data) => {
return request({
url: '/html/htmlMediaInsert/htmlInsertImageCatalog',
method: 'post',
data: data
})
}
/**
* @description 新增图组地址
* @param {Any} data 图组地址列表
* @returns
*/
export const insertPictureUrl = (data) => {
return request({
url: '/html/htmlMediaInsert/htmlBatchInsertEnclosure',
method: 'post',
data: data
})
}
/**
* @description 删除图组
* @param {Any} data 删除信息对象
* @returns
*/
export const deleteAppCatalog = (data) => {
return request({
url: '/app/appMediaDelete/deleteAppCatalog',
method: 'post',
data: data
})
}
/**
* @description 查询图组详情
* @param {Number} id 图组id
* @returns
*/
export const selectPictureById = (id) => {
return request({
url: `/app/appMediaSelect/selectAppByMaterialId/${id}`,
method: 'post',
data: {}
})
}
/**
* @description 查询组织
* @param {Any} data 组织对象
* @returns
*/
export const selectOrganizeList = (data) => {
data.moduleCode = import.meta.env.VITE_APP_VISITOR_CODE
return request({
url: '/html/htmlMediaSelect/selectHtmlOrganizeList',
method: 'post',
data: data
})
}
import request from '@/utils/request'
/**
* @description 专题标签列表查询
* @param {Any} data 标签对象
* @returns
*/
export const selectTagsList = (data) => {
return request({
url: '/app/appMediaSelect/selectAppTagList',
method: 'post',
data: data
})
}
/**
* @description 专题列表查询
* @param {Any} page 分页对象
* @param {Any} data 标签对象
* @returns
*/
export const selectSubjectList = (page,data) => {
return request({
url: `/app/appMediaSelect/selectAppMediaByTagNamePage/${page.pageNum}/${page.pageSize}`,
method: 'post',
data: data
})
}
\ No newline at end of file
@font-face {font-family: "iconfont";
src: url('iconfont.eot?t=1604623478709'); /* IE9 */
src: url('iconfont.eot?t=1604623478709#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
url('iconfont.woff?t=1604623478709') format('woff'),
url('iconfont.ttf?t=1604623478709') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('iconfont.svg?t=1604623478709#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-tubiao201:before {
content: "\e638";
}
.icon-zhengquewanchengchenggong:before {
content: "\e75c";
}
.icon-shanchu:before {
content: "\e612";
}
.icon-close-eye:before {
content: "\e79c";
}
.icon-icon-test:before {
content: "\e640";
}
.icon-yanzhengma2:before {
content: "\e656";
}
.icon-mima:before {
content: "\e633";
}
.icon-shoujihaoma:before {
content: "\e6da";
}
.icon-biaoqing:before {
content: "\e60c";
}
.icon-chenggong:before {
content: "\e75a";
}
.icon-fanhui:before {
content: "\e600";
}
.icon-jiantou1:before {
content: "\e60d";
}
.icon-jinbileyuaniocn:before {
content: "\e614";
}
.icon-tongzhituiguang:before {
content: "\e611";
}
.icon-dianpu:before {
content: "\e895";
}
.icon-denglu:before {
content: "\e66b";
}
.icon-neirongziduanguanli:before {
content: "\e6b2";
}
.icon-dailizhanghaoguanli:before {
content: "\e642";
}
.icon-zhanghao:before {
content: "\e678";
}
.icon-zizhanghaoguanli:before {
content: "\e610";
}
.icon-fafang:before {
content: "\e749";
}
.icon-youhuiquan1:before {
content: "\e659";
}
.icon-fenxiang:before {
content: "\e687";
}
.icon-jiantou_down:before {
content: "\e61a";
}
.icon-tuichu1:before {
content: "\e64b";
}
.icon-huiyuan:before {
content: "\e632";
}
.icon-huiyuan1:before {
content: "\e658";
}
.icon-94:before {
content: "\e75b";
}
.icon-youhuiquan:before {
content: "\e69b";
}
.icon-08_zizhanghaoguanli:before {
content: "\e630";
}
.icon-zhanghuguanli:before {
content: "\e60b";
}
.icon-4:before {
content: "\e623";
}
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const createRouter: typeof import('uni-mini-router')['createRouter']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const effectScope: typeof import('vue')['effectScope']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onAddToFavorites: typeof import('@dcloudio/uni-app')['onAddToFavorites']
const onBackPress: typeof import('@dcloudio/uni-app')['onBackPress']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onError: typeof import('@dcloudio/uni-app')['onError']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onHide: typeof import('@dcloudio/uni-app')['onHide']
const onLaunch: typeof import('@dcloudio/uni-app')['onLaunch']
const onLoad: typeof import('@dcloudio/uni-app')['onLoad']
const onMounted: typeof import('vue')['onMounted']
const onNavigationBarButtonTap: typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']
const onNavigationBarSearchInputChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']
const onNavigationBarSearchInputClicked: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']
const onNavigationBarSearchInputConfirmed: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']
const onNavigationBarSearchInputFocusChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']
const onPageNotFound: typeof import('@dcloudio/uni-app')['onPageNotFound']
const onPageScroll: typeof import('@dcloudio/uni-app')['onPageScroll']
const onPullDownRefresh: typeof import('@dcloudio/uni-app')['onPullDownRefresh']
const onReachBottom: typeof import('@dcloudio/uni-app')['onReachBottom']
const onReady: typeof import('@dcloudio/uni-app')['onReady']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onResize: typeof import('@dcloudio/uni-app')['onResize']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onShareAppMessage: typeof import('@dcloudio/uni-app')['onShareAppMessage']
const onShareTimeline: typeof import('@dcloudio/uni-app')['onShareTimeline']
const onShow: typeof import('@dcloudio/uni-app')['onShow']
const onTabItemTap: typeof import('@dcloudio/uni-app')['onTabItemTap']
const onThemeChange: typeof import('@dcloudio/uni-app')['onThemeChange']
const onUnhandledRejection: typeof import('@dcloudio/uni-app')['onUnhandledRejection']
const onUnload: typeof import('@dcloudio/uni-app')['onUnload']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const showToast: typeof import('vant/es')['showToast']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useLink: typeof import('vue-router')['useLink']
const useRoute: typeof import('uni-mini-router')['useRoute']
const useRouter: typeof import('uni-mini-router')['useRouter']
const useSlots: typeof import('vue')['useSlots']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
const xma: typeof import('rexma-cli')['xma']
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
import('vue')
}
/// <reference types='@dcloudio/types' />
import 'vue'
declare module '@vue/runtime-core' {
type Hooks = App.AppInstance & Page.PageInstance;
interface ComponentCustomOptions extends Hooks {
}
}
/// <reference types="vite/client" />
declare module '*.vue' {
import { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}
export function useInitScroll() {
// 记录离开时导航条的位置
let scrollY = ref();
// 离开路由时的钩子函数
onBeforeRouteLeave((to, from, next) => {
// 记录当前的滚动条位置
scrollY.value =
window.pageYOffset ||
document.documentElement.scrollTop ||
document.body.scrollTop;
next()
})
onActivated(() => {
nextTick(() => { }).then(() => {
window.scroll(0, scrollY.value);
})
})
}
\ No newline at end of file
import { createSSRApp } from "vue";
import App from "./App.vue";
import { createPinia } from 'pinia'
import piniaPersist from 'pinia-plugin-persist'
import router from "./router";
import Vconsole from "vconsole";
import 'vant/es/toast/style'
import 'vant/es/dialog/style'
import 'vant/es/notify/style'
import 'vant/es/image-preview/style'
export function createApp() {
const app = createSSRApp(App);
const pinia = createPinia()
pinia.use(piniaPersist)
app.use(pinia)
app.use(router);
let vConsole = new Vconsole();
app.use(vConsole);
return {
app,
};
}
// #ifdef H5
console.log('当前基准平台:H5')
// #endif
// #ifdef H5-SGY
console.log('当前扩展平台:H5-SGY')
// #endif
{
"name" : "",
"appid" : "",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App特有相关 */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* 模块配置 */
"modules" : {},
/* 应用发布信息 */
"distribute" : {
/* android打包配置 */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios打包配置 */
"ios" : {},
/* SDK配置 */
"sdkConfigs" : {}
}
},
/* 快应用特有相关 */
"quickapp" : {},
/* 小程序特有相关 */
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics": {
"enable": false
},
"vueVersion" : "3",
"h5" : {
"router" : {
"base" : "./",
"mode" : "hash"
},
"sdkConfigs" : {
"maps" : {
"qqmap" : {
"key" : "MN4BZ-7JXKW-2RYRD-32QGF-AHONV-PAFUN"
}
}
},
"optimization" : {
"treeShaking" : {
"enable" : true
}
}
}
}
{
"easycom": {
"autoscan": true,
"custom": {
"^wd-(.*)": "wot-design-uni/components/wd-$1/wd-$1.vue"
}
},
"pages": [ // pages 数组中第一项表示应用启动页
{
"path": "pages/home/index",
"name": "home",
"style": {
"navigationBarTitleText": "甲秀图库"
}
},
{
"path": "pages/classify/index",
"name": "classify",
"style": {
"navigationBarTitleText": "分类",
"navigationStyle": "custom"
}
},
{
"path": "pages/mine/index",
"name": "mine",
"style": {
"navigationBarTitleText": "个人中心"
},
"meta": {
"requireAuth": true
}
},
{
"path": "pages/home/content",
"name": "home-content",
"style": {
"navigationBarTitleText": "图组详情"
}
},
{
"path": "pages/publish/index",
"name": "publish",
"style": {
"navigationBarTitleText": "图组发布"
}
},
{
"path": "pages/publish/result",
"name": "publish-result",
"style": {
"navigationBarTitleText": "发布结果"
},
"meta": {
"requireAuth": true
}
},
{
"path": "pages/prefecture/index",
"name": "prefecture",
"style": {
"navigationBarTitleText": "专区"
},
"meta": {
"requireAuth": true
}
},
{
"path": "pages/subject/index",
"name": "subject",
"style": {
"navigationBarTitleText": "专题"
},
"meta": {
"requireAuth": true
}
},
{
"path": "pages/subject/list",
"name": "subject-list",
"style": {
"navigationBarTitleText": "专题列表"
},
"meta": {
"requireAuth": true
}
},
{
"path": "pages/account/login",
"name": "account-login",
"style": {
"navigationBarTitleText": "登录"
}
},
{
"path": "pages/account/userinfo",
"name": "account-userinfo",
"style": {
"navigationBarTitleText": "用户信息"
},
"meta": {
"requireAuth": true
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#fff",
"backgroundColor": "#F8F8F8",
"app-plus": {
"background": "#efeff4"
}
},
"tabBar": {
"color": "#707070",
"selectedColor": "#0E7CE3",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/home/index",
"iconPath": "static/image/home.png",
"selectedIconPath": "static/image/home-select.png",
"text": "首页"
}, {
"pagePath": "pages/classify/index",
"iconPath": "static/image/classify.png",
"selectedIconPath": "static/image/classify-select.png",
"text": "分类"
}, {
"pagePath": "pages/mine/index",
"iconPath": "static/image/mine.png",
"selectedIconPath": "static/image/mine-select.png",
"text": "我的"
}]
}
}
<template>
<!-- <van-nav-bar title="登录" :left-arrow="true" fixed placeholder safe-area-inset-top @click-left="router.go(-1)" /> -->
<div class="warp clearfloat">
<div class="login-top clearfloat box-s">
<div class="ge-back"><img src="@/static/image/login.png"></div>
<div class="logo-imgs">
<img src="@/static/image/login-circle.png">
</div>
</div>
</div>
<div class="tab-account">
<!-- <ul class="nav nav-tabs login-tabs">
<li :class="{ active: activeIndex == 0 }" @click="activeIndex = 0">
<a data-toggle="tab">登录</a>
</li>
<li :class="{ active: activeIndex == 1 }" @click="activeIndex = 1">
<a data-toggle="tab">注册</a>
</li>
</ul> -->
<div class="tab-content">
<div class="tab-pane fade" v-if="activeIndex == 0">
<form>
<div class="form-group login-group_re">
<i class="iconfont icon-shoujihaoma"></i>
<input type="text" v-model="formData.account" class="form-control inpt-form"
placeholder="请输入手机号" :rules="[{ required: true, message: '请输入手机号' }]">
</div>
<div class="form-group login-group_re">
<i class="iconfont icon-mima"></i>
<input v-model="formData.password" class="form-control inpt-form" type="password"
placeholder="请输入密码" v-if="!passwordVisible">
<input v-model="formData.password" class="form-control inpt-form" type="text"
placeholder="请输入密码" v-else>
<i class="icon-icon-test iconfont xian-mi icon-icon-test" @click="passwordVisible = true"
v-if="!passwordVisible"></i>
<i class="iconfont xian-mi icon-close-eye" @click="passwordVisible = false" v-else></i>
</div>
<div class="login-btns">
<van-button class="btn" type="primary" round @click="login">登录</van-button>
<!-- #ifdef H5-SGY -->
<van-button class="btn" plain type="primary" round
@click="fastLoginBySgy">爽贵阳用户快捷登录</van-button>
<!-- #endif -->
</div>
</form>
</div>
<div class="tab-pane fade in active" v-if="activeIndex == 1">
<form role="form">
<div class="form-group login-group_re">
<i class="iconfont icon-shoujihaoma"></i>
<input type="text" class="form-control inpt-form" placeholder="请输入手机号">
</div>
<div class="form-group login-group_re">
<i class="iconfont icon-yanzhengma2"></i>
<input type="tel" class="form-control inpt-form" placeholder="请输入手机号验证码">
<a href="javascript:;" data-appid="2034800824" data-cbfn="callback"
class="linkABlue yanzheng">获取验证码</a>
</div>
<div class="form-group login-group_re">
<i class="iconfont icon-mima"></i>
<input data-toggle="password" class="form-control inpt-form" type="password"
placeholder="请输入密码">
</div>
<button type="button" class="conf-btn login-btns">注册</button>
</form>
</div>
</div>
</div>
</template>
<script setup>
import { showToast, showLoadingToast, closeToast } from 'vant'
import { loginByPassword, loginBySgy } from '@/api/account'
import useUserStore from '@/store/user'
const router = useRouter()
const userStore = useUserStore()
const activeIndex = ref(0)
const passwordVisible = ref(false)
const formData = ref({
account: '',
password: ''
})
const login = async () => {
if (!formData.value.account) {
showToast('请输入用户名')
return
}
if (!formData.value.password) {
showToast('请输入密码')
return
}
showLoadingToast({
message: '登录中',
forbidClick: true,
duration: 0
})
const res = await loginByPassword(formData.value)
userStore.login(res)
closeToast()
showToast('登录成功')
router.pushTab({ name: 'home' })
}
const sgyLoginParams = ref({
appid: import.meta.env.VITE_APP_SGY_APPID,
type: "sgy",
encryptDataMap: {}
})
const fastLoginBySgy = () => {
xma.xh.getUserProfile({
range: ['ACCOUNT', 'MOBILE', 'CITIZEN'],
async success(info) {
console.log(info)
showLoadingToast({
message: '登录中',
forbidClick: true,
duration: 0
})
sgyLoginParams.value.encryptDataMap = info.data
const res = await loginBySgy(sgyLoginParams.value)
console.log(res)
userStore.login(res)
closeToast()
xma.showToast({
title: '登录成功',
icon: 'success'
})
router.pushTab({ name: 'home' })
}
});
}
</script>
<style scoped>
@import url('@/assets/iconfont/iconfont.css');
.clearfloat:after {
display: block;
clear: both;
content: "";
visibility: hidden;
height: 0
}
.clearfloat {
zoom: 1
}
.box-s {
box-sizing: border-box;
-webkit-box-sizing: border-box;
}
.tab-account .login-tabs {
display: flex;
justify-content: center;
}
.tab-account .login-tabs li {
width: 50%;
text-align: center;
}
.tab-account {
background-color: #fff;
padding: 0;
margin-top: 11%;
}
.tab-account .nav-tabs {
padding: 0 0.4rem;
border-bottom: none !important;
}
.tab-account .nav-tabs li {
width: 35%;
text-align: center;
}
.nav-tabs>li>a {
color: #000;
font-size: 0.45rem;
}
.nav>li>a {
position: relative;
display: block;
padding: 10px 15px;
}
.tab-account .nav-tabs>li.active>a,
.nav-tabs>li.active>a:focus,
.nav-tabs>li.active>a:hover {
color: #4C82FF;
border-color: transparent;
border-bottom: 2px solid #4C82FF !important;
background-color: transparent;
}
.tab-account .tab-content {
margin-top: 10px;
background: #fff;
padding: 0.4rem;
}
.form-control:focus,
.form-control:active,
.form-control:hover {
border-color: #1890ff !important;
box-shadow: none;
}
.conf-btn {
width: 100%;
padding: 10px 20px !important;
border-radius: 50px !important;
border: none;
background: #4c82ff !important;
color: #fff;
font-size: 0.48rem;
}
.login-top {
width: 100%;
height: 6.8rem;
}
.login-top .ge-back img {
width: 100%;
height: 6.4rem;
display: block;
margin: 0 auto;
}
.logo-imgs {
position: relative;
left: 44%;
top: -27%;
width: 3rem;
height: 3rem;
text-align: center;
background: #fff;
border-radius: 16px;
-moz-box-shadow: 2px 2px 5px rgba(76, 130, 255, .4);
-webkit-box-shadow: 2px 2px 5px rgba(76, 130, 255, .4);
box-shadow: 2px 2px 5px rgba(76, 130, 255, .4);
}
.logo-imgs img {
width: 2.2rem;
height: 2.2rem;
margin: 0.4rem 0;
}
.login-group_re {
display: flex;
gap: 10px;
margin-bottom: 3%;
}
.login-group_re i {
line-height: 37px;
color: #4C82FF;
font-size: 1rem;
background: #fff;
}
.login-group_re .input-group-addon {
padding: 0;
background-color: transparent;
border: none;
border-radius: none;
}
.login-group_re .input-group {
width: 100%;
}
.login-group_re .xian-mi {
color: #8a8a8a;
}
.login-group_re .inpt-form {
flex: 1;
border: none !important;
border-bottom: solid 1px #EEE !important;
height: 40px;
outline: none;
line-height: 40px;
background-color: #FFF;
}
.login-group_re .inpt-form:focus {
border-bottom: 1px solid #4C82FF !important;
}
.yanzheng {
line-height: 40px;
color: #4C82FF;
font-size: 0.4rem;
width: 2.5rem;
text-align: center;
font-size: 0.4rem;
}
.yanzheng:focus,
.yanzheng:hover,
.yanzheng:active {
color: #4c82ff;
}
.login-wang {
display: flex;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
justify-content: space-between;
margin-top: 3%;
}
.login-wang a {
font-size: 0.4rem;
}
.login-btns {
margin-top: 4%;
width: 100%;
gap: 15px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.btn {
width: 100%;
}
</style>
\ No newline at end of file
<template>
<van-nav-bar title="用户中心" :left-arrow="true" border fixed placeholder safe-area-inset-top
@click-left="router.go(-1)" />
<div class="container">
<div class="content">
<div class="item">
<van-icon name="friends-o" size="26" />
<div class="title">用户名</div>
<div class="text">
<div>{{ userStore.userinfo?.user.name }}</div>
<van-icon name="arrow" size="18" color="#d1d1d1" />
</div>
</div>
<div class="item">
<van-icon name="phone-o" size="26" />
<div class="title">手机号码</div>
<div class="text">
<div>{{ userStore.userinfo?.user.phone }}</div>
<van-icon name="arrow" size="18" color="#d1d1d1" />
</div>
</div>
<div class="item">
<van-icon name="envelop-o" size="26" />
<div class="title">邮箱</div>
<div class="text">
<div>{{ userStore.userinfo?.user.email }}</div>
<van-icon name="arrow" size="18" color="#d1d1d1" />
</div>
</div>
</div>
<div class="logout">
<van-button type="warning" block @click="logout">退出登录</van-button>
</div>
</div>
</template>
<script setup>
import { showConfirmDialog, showToast } from 'vant'
import useUserStore from "@/store/user"
const router = useRouter()
const userStore = useUserStore()
const logout = () => {
showConfirmDialog({
title: '提示',
message:
'确定退出登录吗',
}).then(() => {
userStore.logout()
showToast('退出登录成功')
router.go(-1)
}).catch(() => {
})
}
</script>
<style lang="scss" scoped>
.container {
padding: 15px 10px;
.content {
background-color: #ffffff;
box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
border-radius: 5px;
.item {
display: flex;
align-items: center;
gap: 10px;
padding: 15px;
border-bottom: 1px solid #e8e8e8;
.title {
flex: 1;
font-size: 16px;
}
.text {
display: flex;
gap: 10px;
font-size: 16px;
color: #0E7CE3;
}
}
}
.logout {
margin-top: 20px;
box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.1);
}
}
</style>
\ No newline at end of file
<template>
<div class="bg">
<van-sticky>
<div class="header">
<div class="title">分类</div>
<div class="search">
<div class="s-box">
<van-icon name="search" color="#303030" />
<input class="placeholder" placeholder="请输入关键词" v-model="value" />
</div>
<van-icon v-if="value" name="clear" color="#969696" />
</div>
</div>
</van-sticky>
<div class="content">
<div class="list">
<div class="item" v-for="(item, index) in 20" :key="index">
<img src="/static/image/cover-1.png">
<div class="blurbox">
<div class="bg"></div>
<div class="title">XXX专题</div>
</div>
</div>
<div class="item">
<img src="/static/image/cover-2.png">
<div class="blurbox">
<div class="bg"></div>
<div class="title">XXX专题</div>
</div>
</div>
<div class="item">
<img src="/static/image/cover-3.png">
<div class="blurbox">
<div class="bg"></div>
<div class="title">XXX专题</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
const value = ref('')
</script>
<style scoped lang="scss">
.bg {
height: 100vh;
background: linear-gradient(to bottom, #D2E4FF 5%, #fff 25%);
.header {
display: flex;
align-items: center;
padding: 0 20px;
height: 50px;
gap: 15px;
background-color: #D2E4FF;
.title {
font-size: 18px;
font-weight: bold;
}
.search {
width: 45%;
background-color: #F7F8FA;
border-radius: 20px;
display: flex;
align-items: center;
justify-content: space-between;
height: 32px;
opacity: 0.7;
padding: 0 10px;
.s-box {
display: flex;
align-items: center;
gap: 5px;
.placeholder {
color: #303030;
font-size: 14px;
}
}
}
}
.content {
padding: 10px 10px 0 10px;
min-height: 100vh;
.list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.item {
break-inside: avoid;
background-color: #ffffff;
border-radius: 5px;
margin-bottom: 10px;
width: 32%;
position: relative;
img {
width: 100%;
height: 170px;
object-fit: cover;
border-radius: 5px;
}
.blurbox {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
bottom: 0;
height: 32px;
width: 100%;
border-radius: 0 0 5px 5px;
.bg {
position: absolute;
border-radius: 0 0 5px 5px;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
}
.title {
font-size: 28rpx;
color: #fff;
font-weight: bold;
z-index: 1;
}
}
}
}
}
}
</style>
<template>
<van-popup v-model:show="show" position="bottom" :close-on-click-overlay="false" safe-area-inset-bottom @close="handleClose">
<div class="container">
<div class="header-placeholder">
<div class="header">
<span class="reset" @click="handleReset">重置</span>
<span class="confirm" @click="handleConfirm">确认</span>
</div>
</div>
<div class="content">
<div class="item" v-for="(item, index) in dataList" :key="index"
:class="{ active: selectItemIndex == index }" @click="selectItemIndex = index">
<span>{{ item.sourceName }}</span>
</div>
</div>
</div>
</van-popup>
</template>
<script setup>
import { selectSourceList } from '@/api/publish';
const emits = defineEmits(["confirm", "close"])
const dataList = ref([])
const getList = async () => {
const res = await selectSourceList(2)
dataList.value = res.data
}
getList()
const selectItemIndex = ref(null)
const handleConfirm = () => {
emits('confirm', dataList.value[selectItemIndex.value])
show.value = false
}
const handleReset = () => {
selectItemIndex.value = null
}
const handleClose = () => {
emits('close')
}
const show = ref(false)
const open = () => {
show.value = true
}
const close = () => {
show.value = false
}
defineExpose({
open,
close
})
</script>
<style lang="scss" scoped>
.container {
max-height: 50vh;
padding: 0 20px;
.header-placeholder {
height: 55px;
.header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 16px;
position: fixed;
width: calc(100vw - 40px);
height: 55px;
background-color: #ffffff;
color: #545454;
.confirm {
color: #0e7ce3;
}
}
}
.content {
display: flex;
flex-wrap: wrap;
gap: 15px;
padding-bottom: 15px;
.item {
background-color: #eef1f3;
flex: 0 0 calc((100vw - 70px) / 3);
padding: 15px 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
color: #676767;
font-size: 15px;
span {
max-width: 80%;
}
}
.active {
background-color: #0e7ce3;
color: #ffffff;
}
}
}
</style>
\ No newline at end of file
<template>
<van-popup v-model:show="show" position="bottom" :close-on-click-overlay="false" safe-area-inset-bottom @close="handleClose">
<div class="container">
<div class="header-placeholder">
<div class="header">
<span class="reset" @click="handleReset">重置</span>
<span class="confirm" @click="handleConfirm">确认</span>
</div>
</div>
<div class="content">
<div class="item" v-for="(item, index) in dataList" :key="index"
:class="{ active: selectItemIndex == index }" @click="selectItemIndex = index">
<span>{{ item.sourceName }}</span>
</div>
</div>
</div>
</van-popup>
</template>
<script setup>
import { selectSourceList } from '@/api/publish';
const emits = defineEmits(["confirm", "close"])
const dataList = ref([])
const getList = async () => {
const res = await selectSourceList(1)
dataList.value = res.data
}
getList()
const selectItemIndex = ref(null)
const handleConfirm = () => {
emits('confirm', dataList.value[selectItemIndex.value])
show.value = false
}
const handleReset = () => {
selectItemIndex.value = null
}
const handleClose = () => {
emits('close')
}
const show = ref(false)
const open = () => {
show.value = true
}
const close = () => {
show.value = false
}
defineExpose({
open,
close
})
</script>
<style lang="scss" scoped>
.container {
max-height: 50vh;
padding: 0 20px;
.header-placeholder {
height: 55px;
.header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 16px;
position: fixed;
width: calc(100vw - 40px);
height: 55px;
background-color: #ffffff;
color: #545454;
.confirm {
color: #0e7ce3;
}
}
}
.content {
display: flex;
flex-wrap: wrap;
gap: 15px;
padding-bottom: 15px;
.item {
background-color: #eef1f3;
flex: 0 0 calc((100vw - 70px) / 3);
padding: 15px 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
color: #676767;
font-size: 15px;
span {
max-width: 80%;
}
}
.active {
background-color: #0e7ce3;
color: #ffffff;
}
}
}
</style>
\ No newline at end of file
<template>
<van-popup v-model:show="show" position="bottom" :close-on-click-overlay="false" safe-area-inset-bottom @close="handleClose">
<div class="container">
<div class="header-placeholder">
<div class="header">
<span class="reset" @click="handleReset">重置</span>
<span class="confirm" @click="handleConfirm">确认</span>
</div>
</div>
<van-cascader v-model="activeId" :show-header="false" :options="treeList" :field-names="fieldNames" />
</div>
</van-popup>
</template>
<script setup>
import { selectTagsList } from '@/api/publish'
const emits = defineEmits(["confirm", "close"])
const activeId = ref(null)
const fieldNames = {
text: 'title',
value: 'id',
children: 'children'
}
const treeList = ref([])
const dataList=ref([])
const getList = async () => {
const res = await selectTagsList()
treeList.value = res.data
dataList.value=dealTreeData(res.data)
}
getList()
const dealTreeData = (data) => {
let arrTree = []
data.forEach((ele) => {
let obj = { ...ele }
delete obj.children
arrTree.push(obj)
if (ele?.children?.length) {
arrTree.push(...dealTreeData(ele.children))
}
});
return arrTree;
}
const handleConfirm = () => {
const item = dataList.value.filter(item => item.id == activeId.value)[0]
emits('confirm', item)
show.value = false
}
const handleReset = () => {
activeId.value = null
}
const handleClose = () => {
emits('close')
}
const show = ref(false)
const open = () => {
show.value = true
}
const close = () => {
show.value = false
}
defineExpose({
open,
close
})
</script>
<style lang="scss" scoped>
.container {
max-height: 50vh;
.header-placeholder {
height: 55px;
.header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 16px;
position: fixed;
width: calc(100vw - 40px);
height: 55px;
background-color: #ffffff;
color: #545454;
padding: 0 20px;
.confirm {
color: #0e7ce3;
}
}
}
::v-deep(.van-sidebar-item) {
text-align: center;
}
}
</style>
\ No newline at end of file
<template>
<van-popup v-model:show="show" position="bottom" :close-on-click-overlay="false" safe-area-inset-bottom @close="handleClose">
<div class="container">
<div class="header-placeholder">
<div class="header">
<span class="reset" @click="handleReset">重置</span>
<span class="confirm" @click="handleConfirm">确认</span>
</div>
</div>
<div class="content">
<div class="item" v-for="(item, index) in dataList" :key="index"
:class="{ active: selectItemIndex == index }" @click="selectItemIndex = index">
<span>{{ item.classifyType }}</span>
</div>
</div>
</div>
</van-popup>
</template>
<script setup>
import { selectTypeList } from '@/api/publish'
const emits = defineEmits(["confirm", "close"])
const dataList = ref([])
const getList = async () => {
const res = await selectTypeList()
dataList.value = res.data
}
getList()
const selectItemIndex = ref(null)
const handleConfirm = () => {
emits('confirm', dataList.value[selectItemIndex.value])
show.value = false
}
const handleReset = () => {
selectItemIndex.value = null
}
const handleClose = () => {
emits('close')
}
const show = ref(false)
const open = () => {
show.value = true
}
const close = () => {
show.value = false
}
defineExpose({
open,
close
})
</script>
<style lang="scss" scoped>
.container {
max-height: 50vh;
padding: 0 20px;
.header-placeholder {
height: 55px;
.header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 16px;
position: fixed;
width: calc(100vw - 40px);
height: 55px;
background-color: #ffffff;
color: #545454;
.confirm {
color: #0e7ce3;
}
}
}
.content {
display: flex;
flex-wrap: wrap;
gap: 15px;
padding-bottom: 15px;
.item {
background-color: #eef1f3;
flex: 0 0 calc((100vw - 70px) / 3);
padding: 15px 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
color: #676767;
font-size: 15px;
span {
max-width: 80%;
}
}
.active {
background-color: #0e7ce3;
color: #ffffff;
}
}
}
</style>
\ No newline at end of file
<template>
<!-- <van-nav-bar title="图组详情" :left-arrow="true" fixed placeholder safe-area-inset-top @click-left="router.go(-1)" /> -->
<div class="container" v-if="catalog">
<div class="info">
<div class="header">
<div class="user">
<img class="avatar" src="../../static/image/avatar-content.png">
<div class="username">{{ catalog.author }}</div>
</div>
<div class="collect">
<van-button icon="plus" loading-size="14" type="primary">收藏</van-button>
</div>
</div>
<div class="tags">
<div class="item" v-for="(tagName, index) in catalog.tagName.split(',')" :key="index">
<span class="tag">{{ tagName }}</span>
</div>
</div>
<div class="content">
<van-row :gutter="[0, 10]">
<van-col span="12">
<div class="item">
<div class="label">渠道:</div>
<div class="value">
<span class="tag">{{ catalog.sourceName }}</span>
</div>
</div>
</van-col>
<van-col span="12">
<div class="item">
<div class="label">类型:</div>
<div class="value">
<span class="tag">{{ catalog.sourceDitch }}</span>
</div>
</div>
</van-col>
<van-col span="12">
<div class="item">
<div class="label">创建人:</div>
<div class="value">{{ catalog.createBy }}</div>
</div>
</van-col>
<van-col span="12">
<div class="item">
<div class="label">组织:</div>
<div class="value">
<van-text-ellipsis :content="catalog.organizeName" />
</div>
</div>
</van-col>
<van-col span="12">
<div class="item">
<div class="label">创建时间:</div>
<div class="value">{{ dayjs(catalog.createtime).format('YYYY-MM-DD') }}</div>
</div>
</van-col>
<van-col span="12">
<div class="item">
<div class="label">数量:</div>
<div class="value">{{ catalog.fileNum }}</div>
</div>
</van-col>
<van-col span="12">
<div class="item">
<div class="label">拍摄时间:</div>
<div class="value">{{ dayjs(catalog.shoottime).format('YYYY-MM-DD') }}</div>
</div>
</van-col>
<van-col span="12">
<div class="item">
<div class="label">大小:</div>
<div class="value">{{ catalog.fileSize }}KB</div>
</div>
</van-col>
<van-col span="24">
<div class="item">
<div class="label">拍摄地点:</div>
<div class="value">{{ catalog.shootLocation }}</div>
</div>
</van-col>
</van-row>
</div>
</div>
<div class="atlas">
<div class="title">{{ catalog.materialTitle }}</div>
<div class="atlas-desc">{{ catalog.materialIntro }}</div>
<div class="list">
<div class="item" v-for="(item, index) in catalog.enclosureList" :key="index">
<img :src="fileDomain + item.compressPath" @click="handlePreviewImage(index)">
<div class="item-desc">
{{ item.enclosureDesc }}
</div>
<div class="item-info">
<!-- <span>808x539</span> -->
<span>{{ item.fileSize }}KB</span>
</div>
</div>
</div>
</div>
</div>
<van-empty description="暂无数据" v-else />
</template>
<script setup>
import dayjs from 'dayjs'
import { showImagePreview } from 'vant';
import { selectPictureById } from '@/api/publish'
const fileDomain = import.meta.env.VITE_APP_FILE_URL
const previewImages = ref([])
const id = ref(0)
onLoad((options) => {
id.value = options.id
handleSelect()
})
const catalog = ref(null)
const handleSelect = async () => {
const res = await selectPictureById(id.value)
catalog.value = res.data
previewImages.value = res.data.enclosureList.map(item => {
return fileDomain + item.watermarkPath
})
}
const handlePreviewImage = (index) => {
showImagePreview({
images: previewImages.value,
startPosition: index,
});
}
</script>
<style lang="scss" scoped>
.container {
background-color: #f6f6f6;
padding: 15px 10px;
display: flex;
flex-direction: column;
gap: 15px;
height: 100vh;
.info {
background-color: #ffffff;
border-radius: 5px;
padding: 20px 15px;
display: flex;
flex-direction: column;
gap: 20px;
box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
.header {
display: flex;
align-items: center;
justify-content: space-between;
.user {
display: flex;
align-items: center;
gap: 10px;
.avatar {
width: 50px;
height: 50px;
border-radius: 50%;
}
.username {
font-size: 17px;
}
}
.collect {
:deep(.van-button--normal) {
padding: 0 5px;
width: 70px;
height: 35px;
font-size: 15px;
.van-button__icon {
font-size: 14px;
}
}
}
}
.tags {
display: flex;
gap: 10px;
flex-wrap: wrap;
font-size: 15px;
}
.content {
font-size: 14px;
.item {
display: flex;
color: #6e737e;
.label {
// width: 75px;
}
.value {
flex: 1;
}
}
}
}
.atlas {
background-color: #ffffff;
border-radius: 10px;
padding: 20px 15px;
display: flex;
flex-direction: column;
gap: 10px;
box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
.title {
font-size: 20px;
overflow-wrap: break-word;
white-space: normal;
}
.atlas-desc {
line-height: 30px;
color: #707070;
font-size: 15px;
overflow-wrap: break-word;
white-space: normal;
}
.list {
column-count: 2;
column-gap: 10px;
.item {
break-inside: avoid;
margin-bottom: 10px;
img {
width: 100%;
object-fit: cover;
}
.item-desc {
font-size: 15px;
color: #707070;
line-height: 25px;
overflow-wrap: break-word;
white-space: normal;
}
.item-info {
font-size: 13px;
color: #707070;
margin-top: 5px;
display: flex;
justify-content: space-between;
}
}
}
}
.tag {
color: #4c70ac;
background-color: #f3f8fe;
padding: 5px 10px;
}
}
</style>
\ No newline at end of file
<template>
<van-sticky>
<div class="header">
<div class="tools">
<van-search v-model="catalog.materialTitle" shape="round" placeholder="请输入搜索关键词" @search="handleRefresh"
@clear="handleRefresh" />
<div class="right" @click="router.push({ name: 'publish' })">
<van-icon name="add" size="26" color="#0E7CE3" />
<span>新建</span>
</div>
</div>
<div class="menu">
<van-tabs v-model:active="tabsActive" line-width="20" title-active-color="#0E7CE3"
title-inactive-color="#82868E" @change="handleRefresh">
<van-tab title="推荐"></van-tab>
<van-tab title="最新"></van-tab>
</van-tabs>
<div class="filtrate">
<div class="item" @click="openTypePopup" :class="{ active: typeActive }">
<span>类型</span>
<van-icon :name="typeActive ? 'arrow-up' : 'arrow-down'" size="13" />
</div>
<div class="item" @click="openCatalogPopup" :class="{ active: tagsActive }">
<span>标签</span>
<van-icon :name="tagsActive ? 'arrow-up' : 'arrow-down'" size="13" />
</div>
<div class="item" @click="openSourcePopup" :class="{ active: sourceChannelActive }">
<span>来源</span>
<van-icon :name="sourceChannelActive ? 'arrow-up' : 'arrow-down'" size="13" />
</div>
</div>
</div>
</div>
</van-sticky>
<div class="content">
<van-pull-refresh v-model="refreshing" @refresh="handleRefresh">
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" :offset="50" @load="getList"
v-if="dataList.length">
<div class="list">
<div class="item" v-for="(item, index) in dataList" :key="index"
@click="router.push({ name: 'home-content', params: { id: item.materialId } })">
<img :src="fileDomain + item.coverpicture">
<div class="desc">
<span class="title">
<van-text-ellipsis :content="item.materialTitle" />
</span>
<div class="tag">
<span v-for="(tagName, index) in item.tagName.split(',')" :key="index">{{ tagName
}}</span>
</div>
<div class="info">
<div class="user">
<img src="../../static/image/avatar.png">
<span>{{ item.author }}</span>
</div>
<div class="collect">
<van-icon name="star-o" color="#9F9F9F" v-if="index < 5" />
<van-icon name="star" color="#F1C13E" v-else />
<span>收藏</span>
</div>
</div>
</div>
</div>
</div>
</van-list>
<van-empty description="暂无数据" v-else />
</van-pull-refresh>
</div>
<type-popup ref="typePopupRef" @confirm="confirmTypePopup" @close="typeActive = false"></type-popup>
<source-channel-popup ref="sourceChannelPopupRef" @confirm="confirmSourceChannelPopup"
@close="sourceChannelActive = false"></source-channel-popup>
<tags-popup ref="tagsPopupRef" @confirm="confirmTagsPopup" @close="tagsActive = false"></tags-popup>
</template>
<script setup>
import TypePopup from '../components/TypePopup/index.vue'
import SourceChannelPopup from '../components/SourceChannelPopup/index.vue'
import TagsPopup from '../components/TagsPopup/index.vue'
import { selectFinePictureList, selectCoarsePictureList } from '@/api/publish'
import dayjs from 'dayjs'
import { useInitScroll } from '@/hooks/useScroll'
useInitScroll()
const router = useRouter()
const tabsActive = ref(0)
const refreshing = ref(false)
const loading = ref(false)
const finished = ref(true)
const fileDomain = import.meta.env.VITE_APP_FILE_URL
const page = reactive({
pageNum: 0,
pageSize: 10
})
const catalog = reactive({
materialTitle: '',
collect: 1,
materialType: 3,
typeClassify: '',
sourceName: '',
organizeInt: [0],
tagId: '',
beginTime: '',
endTime: ''
})
const dataList = ref([])
const handleSelectFindList = async () => {
const res = await selectFinePictureList(page, catalog)
setDataList(res)
}
const handleSelectCoarseList = async () => {
const currentDate = new Date()
const sevenDaysAgo = new Date(currentDate.getTime() - 7 * 24 * 60 * 60 * 1000)
catalog.beginTime = dayjs(sevenDaysAgo).format('YYYY-MM-DD HH:mm:ss')
catalog.endTime = dayjs(currentDate).format('YYYY-MM-DD HH:mm:ss')
const res = await selectCoarsePictureList(page, catalog)
setDataList(res)
}
const getList = () => {
page.pageNum++
loading.value = true
if (tabsActive.value == 0) {
handleSelectFindList()
} else {
handleSelectCoarseList()
}
}
getList()
const handleRefresh = () => {
window.scroll(0,0)
refreshing.value = true
page.pageNum = 0
getList()
}
const setDataList = (res) => {
if (refreshing.value) {
dataList.value = res.data.list
} else {
dataList.value.push(...res.data.list)
}
loading.value = false
finished.value = dataList.value.length >= res.data.total
refreshing.value = false
}
const typeActive = ref(false)
const typePopupRef = ref()
const openTypePopup = () => {
typeActive.value = true
typePopupRef.value.open()
}
const confirmTypePopup = (e) => {
page.pageNum = 0
catalog.typeClassify = e?.classifyType ?? ''
refreshing.value = true
getList()
}
const sourceChannelActive = ref(false)
const sourceChannelPopupRef = ref()
const openSourcePopup = () => {
sourceChannelActive.value = true
sourceChannelPopupRef.value.open()
}
const confirmSourceChannelPopup = (e) => {
page.pageNum = 0
catalog.sourceName = e?.sourceName ?? ''
refreshing.value = true
getList()
}
const tagsActive = ref(false)
const tagsPopupRef = ref()
const openCatalogPopup = () => {
tagsActive.value = true
tagsPopupRef.value.open()
}
const confirmTagsPopup = (e) => {
page.pageNum = 0
catalog.tagId = e?.id.toString() ?? ''
refreshing.value = true
getList()
}
</script>
<style lang="scss" scoped>
.header {
padding: 10px;
background-color: #ffffff;
box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.1);
.tools {
display: flex;
gap: 10px;
:deep(.van-search) {
flex: 1;
padding: 0;
.van-search__content {
height: 100%;
.van-search__field {
height: 100%;
}
}
}
.right {
width: 35px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
span {
font-size: 13px;
color: #0E7CE3;
}
}
}
.menu {
display: flex;
justify-content: space-between;
padding-top: 10px;
:deep(.van-tabs__nav) {
gap: 30px;
padding-bottom: 0;
.van-tab {
font-size: 15px;
padding: 0;
flex: initial;
}
.van-tabs__line {
bottom: 5px;
background: #0E7CE3;
}
}
.filtrate {
display: flex;
align-items: center;
height: 44px;
gap: 10px;
.item {
display: flex;
gap: 2px;
align-items: center;
justify-content: center;
font-size: 14px;
background-color: #e7e8e9;
width: 60px;
height: 24px;
border-radius: 4px;
}
}
}
}
.header-placeholder {
height: 117px;
}
.content {
background-color: #f3f3f3;
padding: 10px 10px 0 10px;
min-height: 100vh;
.list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.item {
break-inside: avoid;
background-color: #ffffff;
border-radius: 5px;
margin-bottom: 10px;
width: 49%;
img {
width: 100%;
height: 150px;
object-fit: cover;
}
.desc {
padding: 5px 10px;
display: flex;
flex-direction: column;
gap: 5px;
.title {
font-size: 16px;
}
.tag {
display: flex;
flex-wrap: wrap;
gap: 3px;
span {
font-size: 14px;
padding: 0 5px;
background-color: #F3F8FF;
color: #4C70AC;
font-size: 12PX;
border-radius: 3px;
}
}
.info {
display: flex;
justify-content: space-between;
padding: 5px 0;
.user {
display: flex;
gap: 7px;
align-items: center;
img {
width: 23px;
height: 23px;
border-radius: 50%;
}
span {
color: #9F9F9F;
font-size: 12px;
}
}
}
.collect {
display: flex;
align-items: center;
gap: 3px;
span {
color: #9F9F9F;
font-size: 12px;
}
}
}
}
}
}
.active {
background-color: #0e7ce3 !important;
color: #ffffff !important;
}
</style>@/utils/useScroll@/hooks/useScroll
\ No newline at end of file
<template>
<!-- #ifdef MP-WEIXIN -->
<view class="container">微信小程序环境</view>
<!-- #endif -->
<!-- #ifdef H5 -->
<view class="container">H5环境</view>
<!-- #endif -->
<!-- #ifdef APP -->
<view class="container">APP环境</view>
<!-- #endif -->
<!-- <van-tabbar v-model="active" :placeholder="true" active-color="#0E7CE3" route>
<van-tabbar-item icon="home-o" to="/home">首页</van-tabbar-item>
<van-tabbar-item icon="apps-o" to="/prefecture">专区</van-tabbar-item>
<van-tabbar-item icon="send-gift-o" to="/subject">专题</van-tabbar-item>
<van-tabbar-item icon="user-o" to="/mine">我的</van-tabbar-item>
</van-tabbar> -->
</template>
<script setup>
import {
ref
} from 'vue'
const active = ref('/home')
// #ifdef H5
console.log('当前环境为H5') // 这里可以编写H5环境的代码逻辑
// #endif
// #ifdef APP
console.log('当前环境为APP') // 这里可以编写APP环境的代码逻辑
// #endif
</script>
<style scoped></style>
\ No newline at end of file
<template>
<van-popup v-model:show="show" position="bottom" round @close="handleClose">
<div class="more">
<div class="item" @click="handleUpdate">编辑图组</div>
<div class="item delete" @click="handleDelete">删除图组</div>
<div class="item" @click="handleClose">取消</div>
</div>
</van-popup>
</template>
<script setup>
import { ref } from 'vue'
import { deleteAppCatalog } from '@/api/publish'
import { showConfirmDialog } from 'vant'
import useUserStore from '@/store/user'
const userStore = useUserStore()
const emits = defineEmits(["refresh"])
const catalog = reactive({
userId: null,
materialId: '',
delBy: ''
})
const handleClose = () => {
show.value = false
}
const handleDelete = () => {
showConfirmDialog({
title: '删除图组',
message: '确定删除该图组吗?',
})
.then(() => {
catalog.userId = userStore.userinfo.user.id
catalog.materialId = pictrueInfo.value.materialId
catalog.delBy = userStore.userinfo.user.name
deleteAppCatalog(catalog).then(res => {
showToast('删除成功')
emits('refresh')
handleClose()
})
})
}
const handleUpdate = () => {
console.log('编辑')
}
const pictrueInfo = ref(null)
const show = ref(false)
const open = (e) => {
console.log(e)
pictrueInfo.value = e
show.value = true
}
const close = () => {
show.value = false
}
defineExpose({
open,
close
})
</script>
<style lang="scss" scoped>
.more {
padding: 10px;
.delete {
color: #E23535;
}
.item {
padding: 10px 0;
text-align: center;
border-bottom: 1px solid #eee;
&:last-child {
border-bottom: none;
}
}
}
</style>
\ No newline at end of file
<template>
<van-pull-refresh v-model="refreshing" @refresh="handleRefresh">
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" :offset="50" @load="getList"
v-if="dataList.length">
<div class="list">
<div class="item" v-for="(item, index) in dataList" :key="index">
<img :src="fileDomain + item.coverpicture"
@click="router.push({ name: 'home-content', params: { id: item.materialId } })">
<div class="desc">
<span class="title">
<van-text-ellipsis :content="item.materialTitle" />
</span>
<div class="tag">
<span v-for="(tagName, index) in item.tagName.split(',')" :key="index">{{
tagName }}</span>
</div>
<div class="info">
<van-button type="primary" size="mini" block @click="handlePublishPicture(item)"
v-if="active == 0">立即发布</van-button>
<div class="user" v-else>
<div class="user-box">
<img src="@/static/image/avatar.png">
<span>{{ item.author }}</span>
</div>
<div v-if="active == 1">
<van-icon name="ellipsis" size="26" color="#545454" @click="handleMore(item)" />
</div>
<div v-if="active == 2">
<van-icon name="star" color="#F1C13E" />
</div>
</div>
</div>
</div>
</div>
</div>
</van-list>
<van-empty description="暂无数据" v-else />
</van-pull-refresh>
</template>
<script setup>
import useUserStore from '@/store/user'
import { selectMinePictureList, selectCollectPictureList } from '@/api/publish'
const props = defineProps({
active: {
type: Number,
default: 0
}
})
const emits = defineEmits(["total", "refresh", "opreation", "publish"])
const router = useRouter()
const userStore = useUserStore()
const refreshing = ref(false)
const loading = ref(false)
const finished = ref(true)
const fileDomain = import.meta.env.VITE_APP_FILE_URL
const page = reactive({
pageNum: 0,
pageSize: 10
})
const catalog = reactive({
userId: null,
materialTitle: '',
collect: 1,
materialType: 3,
typeClassify: '',
sourceName: '',
organizeInt: [],
tagId: '',
beginTime: '',
endTime: ''
})
const dataList = ref([])
const getList = async () => {
page.pageNum++
loading.value = true
catalog.userId = userStore.userinfo.user.id
catalog.collect = props.active == 0 ? 0 : 1
let res = null
if (props.active == 2) {
res = await selectCollectPictureList(page, catalog)
} else {
res = await selectMinePictureList(page, catalog)
}
setDataList(res)
emits('total', { activeIndex: props.active, value: res.data.total })
}
const setDataList = (res) => {
if (refreshing.value) {
dataList.value = res.data.list
} else {
dataList.value.push(...res.data.list)
}
loading.value = false
finished.value = dataList.value.length >= res.data.total
refreshing.value = false
}
getList()
const handleRefresh = () => {
refreshing.value = true
page.pageNum = 0
getList()
}
const handleMore = (e) => {
emits('opreation', e)
}
const handlePublishPicture = (e) => {
emits('publish', e)
}
defineExpose({
refresh: handleRefresh
})
</script>
<style lang="scss" scoped>
.list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-top: 20px;
.item {
break-inside: avoid;
background-color: #ffffff;
border-radius: 5px;
margin-bottom: 10px;
width: 49%;
img {
width: 100%;
height: 150px;
object-fit: cover;
}
.desc {
padding: 5px 10px;
display: flex;
flex-direction: column;
gap: 5px;
.title {
font-size: 16px;
}
.tag {
display: flex;
flex-wrap: wrap;
gap: 3px;
span {
font-size: 14px;
padding: 0 5px;
background-color: #F3F8FF;
color: #4C70AC;
font-size: 12PX;
border-radius: 3px;
}
}
.info {
display: flex;
justify-content: space-between;
padding: 5px 0;
.user {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
.user-box {
display: flex;
gap: 7px;
align-items: center;
img {
width: 23px;
height: 23px;
border-radius: 50%;
}
span {
color: #9F9F9F;
font-size: 12px;
}
}
}
}
.collect {
display: flex;
align-items: center;
gap: 3px;
span {
color: #9F9F9F;
font-size: 12px;
}
}
}
}
}
</style>
\ No newline at end of file
<template>
<van-popup v-model:show="showPublish" round position="bottom" closeable>
<div class="popbox">
<div class="title">发布图组</div>
<van-form @submit="handleSubmit" required colon>
<van-field v-model="formData.organizeName" is-link readonly name="picker" label="选择组织"
placeholder="请选择组织" @click="showOrganize = true" :rules="[{ required: true, message: '请选择组织' }]" />
<div class="btn">
<van-button round type="primary" native-type="submit" size="small">立即发布</van-button>
</div>
</van-form>
</div>
</van-popup>
<van-popup v-model:show="showOrganize" round position="bottom">
<van-cascader v-model="formData.organizeName" title="请选择组织" :options="options" :field-names="fieldNames"
@close="showOrganize = false" @finish="onFinish" />
</van-popup>
</template>
<script setup>
import useUserStore from '@/store/user'
import { publishPicture } from '@/api/publish'
import { selectOrganizeList } from '@/api/prefecture'
import { showLoadingToast, closeToast, showToast } from 'vant'
const emits = defineEmits(["refresh"])
const userStore = useUserStore()
const dataList = ref([])
const options = ref([])
const getList = async () => {
const data = {
userId: userStore.userinfo.user.id,
sysCode: import.meta.env.VITE_APP_SYSTEM_CODE,
moduleCode: 'practice-mediaCatalog'
}
const res = await selectOrganizeList(data)
dataList.value = res.data
options.value = buildTree(res.data)
}
const formData = ref({
materialId: null,
organizeId: '',
organizeName: '',
collect: 1
})
const showOrganize = ref(false)
const fieldNames = {
text: 'organName',
value: 'id',
children: 'children',
};
const buildTree = (items) => {
const tree = [{
id: '0',
organName: '无组织',
children: []
}]
const lookup = {}
// 初始化lookup对象,方便后续快速查找
items.forEach(item => {
lookup[item.id] = { ...item, id: item.id.toString(), children: [] };
});
// 构建树
items.forEach(item => {
if (item.superiorId == 0) {
// superiorId=0的项是顶级项
tree.push(lookup[item.id]);
} else {
// 否则,添加到对应父节点的children数组中
if (!lookup[item.superiorId]) {
// 如果父节点不存在(理论上不应该发生,除非数据有误),可以创建一个空的占位符
lookup[item.superiorId] = { children: [] };
}
lookup[item.superiorId].children.push(lookup[item.id]);
}
});
return removeEmptyChildren(tree)
}
const removeEmptyChildren = (nodes) => {
nodes.forEach(node => {
if (node.children.length === 0) {
delete node.children;
} else {
removeEmptyChildren(node.children);
}
});
return nodes
}
// 全部选项选择完毕后,会触发 finish 事件
const onFinish = ({ selectedOptions }) => {
showOrganize.value = false;
console.log(selectedOptions)
formData.value.organizeId = selectedOptions[selectedOptions.length - 1].id
formData.value.organizeName = selectedOptions[selectedOptions.length - 1].organName
};
const handleSubmit = async () => {
showLoadingToast({
message: '发布中',
forbidClick: true,
duration: 0
})
await publishPicture(formData.value)
closeToast()
showToast('发布成功')
showPublish.value = false
emits('refresh')
}
const showPublish = ref(false)
const open = (e) => {
console.log(e)
formData.value.materialId = e.materialId
showPublish.value = true
getList()
}
const close = () => {
showPublish.value = false
}
defineExpose({
open,
close
})
</script>
<style lang="scss" scoped>
.popbox {
padding: 20px;
.title {
font-size: 18px;
font-weight: bold;
margin-bottom: 20px;
}
.btn {
width: 100%;
display: flex;
justify-content: center;
margin-top: 20px;
.van-button {
width: 100%;
}
}
}
</style>
\ No newline at end of file
<template>
<div class="container" v-if="userStore.isLogin">
<div class="userinfo">
<img class="avatar" mode="aspectFill"
:src="fileDomain + userStore.userinfo.user.headSculpturePhoto ?? '../../static/image/avatar-content.png'"
@click="router.push({ name: 'account-userinfo' })">
<div class="user">
<div class="nickname">{{ userStore.userinfo.user.name }}</div>
<div class="section">
{{ userStore.userinfo.organizeList[0]?.organName ?? '未知部门' }}
</div>
</div>
<div class="exit">
<img src="../../static/image/exit.png" @click="logout">
</div>
</div>
<div class="menu">
<div class="status">
<div class="item">
<div class="num">{{ total[0] }}</div>
<div class="text">待发布</div>
</div>
<div class="item">
<div class="num">{{ total[1] }}</div>
<div class="text">已发布</div>
</div>
<div class="item">
<div class="num">{{ total[2] }}</div>
<div class="text">收藏</div>
</div>
</div>
<van-button type="primary" icon="guide-o" size="small"
@click="router.push({ name: 'publish' })">新建图组</van-button>
</div>
<div class="edit">
<van-button plain type="default" round block
@click="router.push({ name: 'account-userinfo' })">编辑资料</van-button>
</div>
<div class="area">
<div class="a-prefecture a-box" @click="router.push({ name: 'prefecture' })">
<div class="a-text">专区</div>
</div>
<div class="a-subject a-box" @click="router.push({ name: 'subject' })">
<div class="a-text">专题</div>
</div>
</div>
<div class="content">
<van-tabs v-model:active="active" background="#f8f8f8" animated swipeable sticky>
<van-tab title="待发布">
<picture-list ref="waitingPublishPicture" :active="0" @total="setTotal" @refresh="handleRefreshPublishPicture" @publish="handlePublishPicture"></picture-list>
</van-tab>
<van-tab title="已发布">
<picture-list ref="publishPicture" :active="1" @total="setTotal" @opreation="handleOperation"></picture-list>
</van-tab>
<van-tab title="收藏">
<picture-list :active="2" @total="setTotal"></picture-list>
</van-tab>
</van-tabs>
</div>
</div>
<MoreOperations ref="morePopupRef" @refresh="handleRefreshPublishPicture"></MoreOperations>
<Publish ref="publishPopupRef" @refresh="handleRefreshWaitingPublishPicture"></Publish>
</template>
<script setup>
import { showConfirmDialog, showToast } from 'vant'
import useUserStore from '@/store/user'
import PictureList from './components/PictureList'
import { useInitScroll } from '@/hooks/useScroll'
import MoreOperations from './components/MoreOperations/index.vue'
import Publish from './components/Publish/index.vue'
useInitScroll()
const router = useRouter()
const userStore = useUserStore()
const active = ref(0)
const fileDomain = import.meta.env.VITE_APP_FILE_URL
const total = reactive({
0: 0,
1: 0,
2: 0
})
onShow(() => {
toLogin()
})
const toLogin = () => {
if (userStore.isLogin())
return
router.push({ name: 'account-login' })
}
const setTotal = (e) => {
total[e.activeIndex] = e.value
}
const publishPicture = ref()
const handleRefreshPublishPicture = () => {
publishPicture.value.refresh()
}
const publishPopupRef = ref()
const handlePublishPicture = (e) => {
publishPopupRef.value.open(e)
}
const waitingPublishPicture = ref()
const handleRefreshWaitingPublishPicture = () => {
handleRefreshPublishPicture()
waitingPublishPicture.value.refresh()
}
const logout = () => {
showConfirmDialog({
title: '提示',
message:
'确定退出登录吗',
}).then(() => {
userStore.logout()
showToast('退出登录成功')
router.push({ name: 'account-login' })
}).catch(() => {
})
}
const morePopupRef = ref()
const handleOperation = (e) => {
morePopupRef.value.open(e)
}
</script>
<style lang="scss" scoped>
.container {
background-color: #f8f8f8;
padding: 38px 12px 0 12px;
min-height: 100vh;
.userinfo {
display: flex;
.avatar {
width: 58px;
height: 58px;
border-radius: 50%;
}
.user {
flex: 1;
margin-left: 16px;
display: flex;
flex-direction: column;
justify-content: space-around;
overflow: hidden;
.nickname {
font-size: 19px;
color: #232524;
}
.section {
width: max-content;
height: 20px;
padding: 0 10px;
line-height: 22px;
text-align: center;
color: #ffffff;
font-size: 13px;
background-color: #232524;
border-radius: 4px;
max-width: calc(100% - 20px);
}
}
.exit {
display: flex;
align-items: center;
img {
width: 28px;
height: 28px;
}
}
}
.menu {
display: flex;
margin-top: 30px;
.status {
display: flex;
flex: 1;
gap: 25px;
.item {
text-align: center;
.num {
font-size: 15px;
}
.text {
font-size: 10px;
color: #707070;
}
}
}
}
.edit {
margin-top: 20px;
.van-button {
background: #f8f8f8;
}
}
.area {
display: flex;
justify-content: space-between;
margin-top: 20px;
.a-box {
width: 344rpx;
height: 120rpx;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.a-prefecture {
background-image: url('../../static/image/prefecture.png');
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
.a-subject {
background-image: url('../../static/image/subject.png');
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
.a-text {
font-size: 32rpx;
color: #232524;
}
}
.content {
margin-top: 20px;
}
}
</style>@/hooks/useScroll
\ No newline at end of file
<template>
<van-popup v-model:show="show" position="left" :style="{ width: '65%', height: '100%' }">
<div class="list">
<div class="container" v-for="(item, index) in dataList" :key="index" @click="handleChoose(item,index)">
<div class="list-item">
<img class="left-logo" src="../../../static/image/organization-logo.png">
<span>{{ item.organName }}</span>
<!-- <van-icon name="arrow-down" size="20" /> -->
<van-icon name="success" size="25" color="#2879FE" v-if="active == index" />
</div>
<!-- <div class="children">
<div class="list-item">
<div class="left-logo"></div>
<span>贵阳日报</span>
<van-icon name="arrow-up" size="20" />
</div>
<div class="list-item">
<div class="left-logo"></div>
<span>摄影部</span>
<van-icon name="arrow-up" size="20" />
</div>
<div class="list-item">
<div class="left-logo"></div>
<span class="active">新媒体运营中心</span>
<van-icon name="success" size="25" color="#2879FE" />
</div>
<div class="list-item">
<div class="left-logo"></div>
<span>技术部</span>
<van-icon name="arrow-up" size="20" />
</div>
</div> -->
</div>
</div>
</van-popup>
</template>
<script setup>
import { selectOrganizeList } from '@/api/prefecture'
import useUserStore from '@/store/user'
import index from '../index.vue';
const show = ref(false)
const userStore = useUserStore()
const emits = defineEmits(["confirm"])
const active = ref(0)
const dataList = ref([])
const getList = async () => {
const data = {
userId: userStore.userinfo.user.id,
sysCode: import.meta.env.VITE_APP_SYSTEM_CODE,
moduleCode: 'practice-mediaCatalog'
}
const res = await selectOrganizeList(data)
dataList.value = res.data
emits('confirm', dataList.value[0])
}
getList()
const handleChoose = (item, index) => {
active.value = index
emits('confirm', item)
close()
}
const open = () => {
show.value = true
}
const close = () => {
show.value = false
}
defineExpose({
open,
close
})
</script>
<style lang="scss" scoped>
.list {
padding: 50px 10px;
.list-item {
display: flex;
align-items: center;
gap: 5px;
padding: 10px 0;
border-bottom: 1px solid #f2F2F2;
.left-logo {
width: 25px;
height: 25px;
}
.active {
color: #2879FE;
}
span {
font-size: 15px;
color: #232524;
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
</style>
\ No newline at end of file
<template>
<van-sticky>
<div class="header">
<div class="tools">
<van-search v-model="queryParams.materialTitle" shape="round" placeholder="请输入搜索关键词"
@search="handleRefresh" @clear="handleRefresh" />
</div>
<div class="menu">
<div class="organization" @click="openOrganizationPopup">
<img src="../../static/image/prefecture-logo1.png">
<span>{{ organize.organName }}</span>
</div>
<div class="filtrate">
<div class="item" @click="openTypePopup" :class="{ active: typeActive }">
<span>类型</span>
<van-icon :name="typeActive ? 'arrow-up' : 'arrow-down'" size="13" />
</div>
<div class="item" @click="openCatalogPopup" :class="{ active: tagsActive }">
<span>标签</span>
<van-icon :name="tagsActive ? 'arrow-up' : 'arrow-down'" size="13" />
</div>
<div class="item" @click="openSourcePopup" :class="{ active: sourceChannelActive }">
<span>来源</span>
<van-icon :name="sourceChannelActive ? 'arrow-up' : 'arrow-down'" size="13" />
</div>
</div>
</div>
</div>
</van-sticky>
<div class="content">
<van-pull-refresh v-model="refreshing" @refresh="handleRefresh">
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" :offset="50" @load="getList"
v-if="dataList.length">
<div class="list">
<div class="item" v-for="(item, index) in dataList" :key="index"
@click="router.push({ name: 'home-content', params: { id: item.materialId } })">
<img :src="fileDomain + item.coverpicture">
<div class="desc">
<span class="title">
<van-text-ellipsis :content="item.materialTitle" />
</span>
<div class="tag">
<span v-for="(tagName, index) in item.tagName.split(',')" :key="index">{{ tagName
}}</span>
</div>
<div class="info">
<div class="user">
<img src="../../static/image/avatar.png">
<span>{{ item.author }}</span>
</div>
<div class="collect">
<van-icon name="star-o" color="#9F9F9F" v-if="index < 5" />
<van-icon name="star" color="#F1C13E" v-else />
<span>收藏</span>
</div>
</div>
</div>
</div>
</div>
</van-list>
<van-empty description="暂无数据" v-else />
</van-pull-refresh>
</div>
<type-popup ref="typePopupRef" @confirm="confirmTypePopup" @close="typeActive = false"></type-popup>
<source-channel-popup ref="sourceChannelPopupRef" @confirm="confirmSourceChannelPopup"
@close="sourceChannelActive = false"></source-channel-popup>
<tags-popup ref="tagsPopupRef" @confirm="confirmTagsPopup" @close="tagsActive = false"></tags-popup>
<organization-popup ref="organizationPopupRef" @confirm="confirmOrganizationPopup"></organization-popup>
</template>
<script setup>
import OrganizationPopup from './components/OrganizationPopup.vue'
import TypePopup from '../components/TypePopup/index.vue'
import SourceChannelPopup from '../components/SourceChannelPopup/index.vue'
import TagsPopup from '../components/TagsPopup/index.vue'
import { selectPrefecturetList } from '@/api/prefecture'
import { useInitScroll } from '@/hooks/useScroll'
useInitScroll()
const router = useRouter()
const refreshing = ref(false)
const loading = ref(false)
const finished = ref(true)
const fileDomain = import.meta.env.VITE_APP_FILE_URL
const organize = ref({})
const page = reactive({
pageNum: 0,
pageSize: 10
})
const queryParams = reactive({
materialTitle: '',
materialType: 3,
organizeId: 1720,
typeClassify: null,
tagId: null,
sourceName: null
})
const dataList = ref([])
const getList = async () => {
page.pageNum++
loading.value = true
const res = await selectPrefecturetList(page, queryParams)
setDataList(res)
}
const setDataList = (res) => {
if (refreshing.value) {
dataList.value = res.data.list
} else {
dataList.value.push(...res.data.list)
}
loading.value = false
finished.value = dataList.value.length >= res.data.total
refreshing.value = false
}
const handleRefresh = () => {
refreshing.value = true
page.pageNum = 0
getList()
}
const typeActive = ref(false)
const typePopupRef = ref()
const openTypePopup = () => {
typeActive.value = true
typePopupRef.value.open()
}
const confirmTypePopup = (e) => {
page.pageNum = 0
queryParams.typeClassify = e?.classifyType ?? ''
refreshing.value = true
getList()
}
const sourceChannelActive = ref(false)
const sourceChannelPopupRef = ref()
const openSourcePopup = () => {
sourceChannelActive.value = true
sourceChannelPopupRef.value.open()
}
const confirmSourceChannelPopup = (e) => {
page.pageNum = 0
queryParams.sourceName = e?.sourceName ?? ''
refreshing.value = true
getList()
}
const tagsActive = ref(false)
const tagsPopupRef = ref()
const openCatalogPopup = () => {
tagsActive.value = true
tagsPopupRef.value.open()
}
const confirmTagsPopup = (e) => {
page.pageNum = 0
queryParams.tagId = e?.id.toString() ?? ''
refreshing.value = true
getList()
}
const organizationPopupRef = ref()
const openOrganizationPopup = () => {
organizationPopupRef.value.open()
}
const confirmOrganizationPopup = (item) => {
organize.value = item
queryParams.organizeId = item?.id
handleRefresh()
}
</script>
<style lang="scss" scoped>
.header {
padding: 10px;
background-color: #ffffff;
box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.1);
.tools {
:deep(.van-search) {
flex: 1;
padding: 0;
}
}
.menu {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 10px;
.organization {
display: flex;
align-items: center;
gap: 3px;
img {
width: 21px;
height: 21px;
}
span {
line-height: 21px;
width: 100px;
font-size: 15px;
color: #545454;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.filtrate {
display: flex;
align-items: center;
height: 44px;
gap: 10px;
.item {
display: flex;
gap: 2px;
align-items: center;
justify-content: center;
font-size: 14px;
background-color: #e7e8e9;
width: 60px;
height: 24px;
border-radius: 4px;
}
}
}
}
.header-placeholder {
height: 117px;
}
.content {
background-color: #f3f3f3;
padding: 10px 10px 0 10px;
min-height: 100vh;
.list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.item {
break-inside: avoid;
background-color: #ffffff;
border-radius: 5px;
margin-bottom: 10px;
width: 49%;
img {
width: 100%;
height: 150px;
object-fit: cover;
}
.desc {
padding: 5px 10px;
display: flex;
flex-direction: column;
gap: 5px;
.title {
font-size: 16px;
}
.tag {
display: flex;
flex-wrap: wrap;
gap: 3px;
span {
font-size: 14px;
padding: 0 5px;
background-color: #F3F8FF;
color: #4C70AC;
font-size: 12PX;
border-radius: 3px;
}
}
.info {
display: flex;
justify-content: space-between;
padding: 5px 0;
.user {
display: flex;
gap: 7px;
align-items: center;
img {
width: 23px;
height: 23px;
border-radius: 50%;
}
span {
color: #9F9F9F;
font-size: 12px;
}
}
}
.collect {
display: flex;
align-items: center;
gap: 3px;
span {
color: #9F9F9F;
font-size: 12px;
}
}
}
}
}
}
.active {
background-color: #0e7ce3 !important;
color: #ffffff !important;
}
</style>@/hooks/useScroll
\ No newline at end of file
<template>
<div class="container">
<van-form @submit="handleSubmit" required colon>
<div class="content">
<div class="header">
<span class="title">基本信息</span>
<div class="title-line"></div>
</div>
<div class="form">
<!-- <van-field v-model="formData.area" is-link readonly label="拍摄地区" placeholder="请选择拍摄地区"
@click="showArea = true" :rules="[{ required: true, message: '请选择拍摄地区' }]" />
<van-field v-model="formData.address" label="详细地址" placeholder="请填写拍摄详细地址"
:rules="[{ required: true, message: '请填写拍摄详细地址' }]" /> -->
<van-field v-model="formData.shootLocation" is-link readonly label="拍摄地区" placeholder="请选择拍摄地区"
@click="handleRegionPicker" :rules="[{ required: true, message: '请选择拍摄地区' }]" />
<van-field v-model="formData.shoottime" is-link readonly label="拍摄时间" placeholder="请选择拍摄时间"
@click=" showShoottimeCalendar = true" :rules="[{ required: true, message: '请选择拍摄时间' }]" />
<van-field v-model="formData.materialtime" is-link readonly label="入库时间" placeholder="请选择入库时间"
@click="showMaterialtimeCalendar = true" :rules="[{ required: true, message: '请选择入库时间' }]" />
<van-field v-model="formData.materialTitle" label="图组标题" placeholder="请填写图组标题"
:rules="[{ required: true, message: '请填写图组标题' }]" />
<van-field v-model="formData.materialIntro" type="textarea" label="图组描述" placeholder="请填写图组描述"
:rules="[{ required: true, message: '请填写图组描述' }]" />
</div>
</div>
<div class="footer" ref="footerRef">
<div>
<van-button plain hairline round type="primary" @click="handlePre">上一步</van-button>
</div>
<div>
<van-button round type="primary" native-type="submit">提交</van-button>
</div>
</div>
</van-form>
</div>
<!-- <van-popup v-model:show="showArea" position="bottom">
<van-area :area-list="areaList" @confirm="areaConfirm" @cancel="showArea = false" />
</van-popup> -->
<van-calendar v-model:show="showShoottimeCalendar" :min-date="minDate" @confirm="shoottimeCalendarConfirm" />
<van-calendar v-model:show="showMaterialtimeCalendar" :min-date="minDate" @confirm="materialtimeCalendarConfirm" />
</template>
<script setup>
import { areaList } from '@vant/area-data'
import dayjs from 'dayjs'
const emits = defineEmits(["confirm"])
const footerRef = ref()
const formData = ref({})
const showArea = ref(false)
const showShoottimeCalendar = ref(false)
const showMaterialtimeCalendar = ref(false)
const minDate = ref(new Date('1970-01-01'))
const handlePre = () => {
emits('confirm', { code: 'pre' })
}
const handleSubmit = () => {
// formData.value.shootLocation = formData.value.area + formData.value.address
emits('confirm', { code: 'submit', catalog: formData.value })
}
const areaConfirm = (selectedOptions) => {
showArea.value = false;
formData.value.area = selectedOptions.selectedOptions.map((item) => item.text).join('');
}
const shoottimeCalendarConfirm = (data) => {
showShoottimeCalendar.value = false
formData.value.shoottime = dayjs(data).format('YYYY-MM-DD HH:mm:ss')
}
const materialtimeCalendarConfirm = (data) => {
showMaterialtimeCalendar.value = false
formData.value.materialtime = dayjs(data).format('YYYY-MM-DD HH:mm:ss')
}
const handleRegionPicker = () => {
xma.chooseLocation({
success: function (res) {
formData.value.shootLocation = res.address
},
fail: function (err) {
console.log(err)
}
});
}
</script>
<style lang="scss" scoped>
.container {
padding-top: 10px;
background-color: #f6f6f6;
.content {
padding: 10px;
background-color: #ffffff;
.header {
display: flex;
font-size: 16px;
flex-direction: column;
align-items: center;
gap: 6px;
.title {
color: #545454;
}
.title-line {
background-color: #2879fe;
height: 4px;
width: 15px;
border-radius: 5px;
}
}
.form {
margin-top: 10px;
.van-cell-group--inset {
margin: 0;
.van-cell {
margin-top: 10px;
font-size: 15px;
}
}
}
}
.footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #ffffff;
display: flex;
justify-content: space-between;
padding: 15px 10px;
box-shadow: -1px -1px 5px rgba(0, 0, 0, 0.1);
align-items: center;
height: 38px;
z-index: 999;
.info {
font-size: 14px;
display: flex;
flex-direction: column;
justify-content: center;
.info-content {
color: #2879fe;
}
}
:deep(.van-button--normal) {
padding: 0 20px;
width: 90px;
height: 35px;
font-size: 15px;
}
}
}
</style>
\ No newline at end of file
<template>
<div class="container">
<div class="content">
<div class="header">
<span class="title">图组上传</span>
<span class="btn" @click="handleUpload"><van-icon name="plus" size="14" />新增图片</span>
<!-- <input type="file" hidden ref="inputRef" accept="image/*" @change="changeFile" :multiple="true"> -->
</div>
<div class="list">
<div class="item" v-for="(item, index) in fileList" :key="index">
<div class="item-content">
<div class="logo">
<img :src="item.src">
<div class="close">
<img src="@/static/image/close.png" @click="handleDelete(index)">
</div>
</div>
<div class="info">
<div class="info-box">
<span class="title">图片{{ index + 1 }}描述:</span>
<div>
<radio-group @change="radioChange" class="radio-box">
<span class="title">封面</span>
<radio style="transform: scale(0.6);" :value="index"
:checked="index == current" />
</radio-group>
</div>
</div>
<textarea v-model="item.enclosureDesc" rows="2" placeholder="请输入图片描述"
:disabled="!item.uploadResult" />
</div>
</div>
<van-progress :percentage="item.progress" v-if="!item.uploadResult" />
</div>
</div>
</div>
<div class="footer-placeholder">
<div class="footer" ref="footerRef">
<div class="info">
<div>
<span>图片总大小:</span>
<span class="info-content">{{ fileSize }}KB</span>
</div>
<div>
<span>图片总容量:</span>
<span class="info-content">{{ fileList.length }}</span>
</div>
</div>
<div>
<van-button round type="primary" @click="handleNext">下一步</van-button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { uploadFile } from '@/api/publish'
import { showToast } from 'vant'
import useUserStore from "@/store/user"
const userStore = useUserStore()
const footerRef = ref()
const inputRef = ref()
const fileList = ref([])
const fileSize = ref(0)
const handleUpload = () => {
// inputRef.value.click()
xma.chooseImage({
count: 20, //默认9
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album'], //从相册选择
success: function (res) {
console.log(res.tempFiles);
const files = res.tempFiles
for (let i = 0; i < files.length; i++) {
const item = files[i]
const index = fileList.value.push(item) - 1
let reader = new FileReader()
reader.readAsDataURL(item)
reader.onload = async (e) => {
item.src = e.target.result
fileList.value.splice(index, 1, { ...item })
item.uploadResult = await uploadFile(item, (e) => {
item.progress = Math.floor(
(e.loaded / e.total) * 100,
)
fileList.value.splice(index, 1, { ...item })
})
fileSize.value += parseInt(item.uploadResult.data.compressNameAndPath[0].size)
fileList.value.splice(index, 1, { ...item })
}
}
},
});
}
const current = ref(0)
const radioChange = (evt) => {
current.value = evt.detail.value
console.log(current.value)
}
const handleDelete = (index) => {
// 当删除的是最后一张图片且是封面图的时候,更换封面图为第一张
if (index + 1 == fileList.value.length && index == current.value) {
current.value = 0
}
fileList.value.splice(index, 1)
}
const emits = defineEmits(["confirm"])
const handleNext = () => {
if (!fileList.value.length) {
showToast('请上传图片')
return
}
const enclosureLists = []
for (let i = 0; i < fileList.value.length; i++) {
const item = fileList.value[i]
if (!item.enclosureDesc) {
showToast(`请输入图片${i + 1}的描述`)
return
}
enclosureLists.push({
mediacatalogId: null,
enclosureName: item.uploadResult.data.compressNameAndPath[0].name,
enclosureType: 3,
enclosureAddr: item.uploadResult.data.compressNameAndPath[0].path,
enclosureDesc: item.enclosureDesc,
createBy: userStore.userinfo.user.name,
fileSize: item.uploadResult.data.compressNameAndPath[0].size,
compressPath: item.uploadResult.data.compressPath[0],
watermarkPath: item.uploadResult.data.waterpath[0]
})
}
emits('confirm', { code: 'next', enclosureLists: enclosureLists, coverIndex: current.value })
}
</script>
<style lang="scss" scoped>
.container {
padding-top: 10px;
background-color: #f6f6f6;
min-height: 100vh;
.content {
padding: 10px;
background-color: #ffffff;
.header {
display: flex;
justify-content: space-between;
font-size: 16px;
.title {
color: #545454;
}
.btn {
color: #2879fe;
}
}
.list {
margin-top: 20px;
display: flex;
flex-direction: column;
gap: 10px;
.item {
.item-content {
display: flex;
gap: 10px;
.logo {
// flex: 0 0 35%;
width: 120px;
height: 90px;
position: relative;
img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 5px;
}
.close {
position: absolute;
top: 5px;
right: 5px;
img {
width: 18px;
height: 18px;
}
}
}
.info {
flex: 1;
display: flex;
flex-direction: column;
gap: 10px;
box-sizing: border-box;
.info-box {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
.radio-box {
display: flex;
align-items: center;
font-size: 28rpx;
color: #545454;
margin-bottom: 20rpx;
}
}
.title {
color: #545454;
font-size: 15px;
}
textarea {
border: 1px solid #cccccc;
flex: 1;
border-radius: 5px;
font-size: 14px;
padding: 10px;
width: 100%;
box-sizing: border-box;
resize: none;
}
}
}
}
.van-progress {
width: 35%;
margin-top: 5px;
}
}
}
.footer-placeholder {
height: 68px;
.footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #ffffff;
display: flex;
justify-content: space-between;
padding: 15px 10px;
box-shadow: -1px -1px 5px rgba(0, 0, 0, 0.1);
align-items: center;
height: 38px;
z-index: 999;
.info {
font-size: 14px;
display: flex;
flex-direction: column;
justify-content: center;
.info-content {
color: #2879fe;
}
}
:deep(.van-button--normal) {
padding: 0 20px;
height: 35px;
font-size: 15px;
}
}
}
}
</style>
\ No newline at end of file
<template>
<div class="container">
<van-form @submit="handleNext" required colon>
<div class="content">
<div class="header">
<span class="title">基本信息</span>
<div class="title-line"></div>
</div>
<div class="form">
<van-field v-model="formData.author" name="作者" label="作者" placeholder="请填写作者"
:rules="[{ required: true, message: '请填写作者' }]" />
<van-field v-model="formData.typeClassify" is-link readonly name="picker" label="类型" placeholder="请选择类型"
@click="openTypePopup" :rules="[{ required: true, message: '请选择类型' }]" />
<van-field v-model="formData.sourceDitch" is-link readonly name="picker" label="来源类型"
placeholder="请选择来源类型" @click="openSourceTypePopup"
:rules="[{ required: true, message: '请选择来源类型' }]" />
<van-field v-model="formData.sourceName" is-link readonly name="picker" label="来源渠道"
placeholder="请选择来源渠道" @click="openSourceChannelPopup"
:rules="[{ required: true, message: '请选择来源渠道' }]" />
<van-field v-model="formData.tagName" is-link readonly name="picker" label="标签" placeholder="请选择标签"
@click="openTagsPopup" :rules="[{ required: true, message: '请选择标签' }]" />
</div>
</div>
<div class="footer" ref="footerRef">
<div>
<van-button plain hairline round type="primary" @click="handlePre">上一步</van-button>
</div>
<div>
<van-button round type="primary" native-type="submit">下一步</van-button>
</div>
</div>
</van-form>
</div>
<type-popup ref="typePopupRef" @confirm="confirmTypePopup"></type-popup>
<source-type-popup ref="sourceTypePopupRef" @confirm="confirmSourceTypePopup"></source-type-popup>
<source-channel-popup ref="sourceChannelPopupRef" @confirm="confirmSourceChannelPopup"></source-channel-popup>
<tags-popup ref="tagsPopupRef" @confirm="confirmTagsPopup"></tags-popup>
</template>
<script setup>
import TypePopup from '../../../components/TypePopup/index.vue'
import SourceTypePopup from '../../../components/SourceTypePopup/index.vue'
import SourceChannelPopup from '../../../components/SourceChannelPopup/index.vue'
import TagsPopup from '../../../components/TagsPopup/index.vue'
import useUserStore from "@/store/user.js"
const emits = defineEmits(["confirm"])
const footerRef = ref()
const userStore = useUserStore()
const formData = ref({
createBy: userStore.userinfo?.user?.name,
author: userStore.userinfo?.user?.name,
userId: userStore.userinfo?.user?.id
})
const handlePre = () => {
emits('confirm', { code: 'pre' })
}
const handleNext = () => {
emits('confirm', { code: 'next', catalog: formData.value })
}
const typePopupRef = ref()
const openTypePopup = () => {
typePopupRef.value.open()
}
const confirmTypePopup = (e) => {
formData.value.typeClassify = e.classifyType
}
const sourceTypePopupRef = ref()
const openSourceTypePopup = () => {
sourceTypePopupRef.value.open()
}
const confirmSourceTypePopup = (e) => {
formData.value.sourceDitch = e.sourceName
}
const sourceChannelPopupRef = ref()
const openSourceChannelPopup = () => {
sourceChannelPopupRef.value.open()
}
const confirmSourceChannelPopup = (e) => {
formData.value.sourceName = e.sourceName
}
const tagsPopupRef = ref()
const openTagsPopup = () => {
tagsPopupRef.value.open()
}
const confirmTagsPopup = (e) => {
formData.value.tagId = e.id
formData.value.tagName = e.title
}
</script>
<style lang="scss" scoped>
.container {
padding-top: 10px;
background-color: #f6f6f6;
.content {
padding: 10px;
background-color: #ffffff;
.header {
display: flex;
font-size: 16px;
flex-direction: column;
align-items: center;
gap: 6px;
.title {
color: #545454;
}
.title-line {
background-color: #2879fe;
height: 4px;
width: 15px;
border-radius: 5px;
}
}
.form {
margin-top: 10px;
.van-cell-group--inset {
margin: 0;
.van-cell {
margin-top: 10px;
font-size: 15px;
}
}
}
}
.footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #ffffff;
display: flex;
justify-content: space-between;
padding: 15px 10px;
box-shadow: -1px -1px 5px rgba(0, 0, 0, 0.1);
align-items: center;
height: 38px;
z-index: 999;
.info {
font-size: 14px;
display: flex;
flex-direction: column;
justify-content: center;
.info-content {
color: #2879fe;
}
}
:deep(.van-button--normal) {
padding: 0 20px;
height: 35px;
font-size: 15px;
}
}
}
</style>
\ No newline at end of file
<template>
<van-nav-bar title="图组" :left-arrow="true" fixed placeholder safe-area-inset-top @click-left="router.go(-1)" />
<div class="container">
<div class="steps-placeholder" :style="{ height: `${stepsRef?.offsetHeight}px` }">
<div ref="stepsRef" class="steps-container">
<van-steps :active="stepsActive" active-color="#2879fe" inactive-color="#b7bdc6">
<van-step>图组上传</van-step>
<van-step>基本信息</van-step>
<van-step>图组信息</van-step>
</van-steps>
</div>
</div>
<keep-alive>
<component :is="componentName" @confirm="handleConfirm"></component>
</keep-alive>
</div>
</template>
<script setup>
import AtlasUpload from './components/AtlasUpload/index.vue'
import BasicInfo from './components/BasicInfo/index.vue'
import AtlasInfo from './components/AtlasInfo/index.vue'
import { insertPicture, insertPictureUrl } from '@/api/publish'
import { showLoadingToast, closeToast } from 'vant'
const router = useRouter()
const stepsActive = ref(0)
const stepsRef = ref()
const componentList = [AtlasUpload, BasicInfo, AtlasInfo]
let componentName = AtlasUpload
const enclosureLists = ref([])
const catalog = ref({
materialSource: 1,
materialType: 3,
checkState: 0,
collect: 0
})
const coverIndex = ref(0)
const handleConfirm = async (data) => {
if (data.code == 'next') {
stepsActive.value++
componentName = componentList[stepsActive.value]
enclosureLists.value = data.enclosureLists ?? enclosureLists.value
catalog.value = data.catalog ? { ...catalog.value, ...data.catalog } : catalog.value
coverIndex.value = data.coverIndex ?? coverIndex.value
}
if (data.code == 'pre') {
stepsActive.value--
componentName = componentList[stepsActive.value]
}
if (data.code == 'submit') {
showLoadingToast({
message: '提交中',
forbidClick: true,
duration: 0
})
catalog.value = data.catalog ? { ...catalog.value, ...data.catalog } : catalog.value
setCatalog()
const res = await insertPicture(catalog.value)
enclosureLists.value.forEach(item => {
item.mediacatalogId = res.data
})
await insertPictureUrl({ enclosureLists: enclosureLists.value })
closeToast()
router.replace({ name: 'publish-result' })
}
}
const setCatalog = () => {
let materialFilePath = []
let compressPath = []
let watermarkPath = []
let fileSize = 0
catalog.value.materialFilePath = enclosureLists.value.forEach(item => {
materialFilePath.push(item.enclosureAddr)
compressPath.push(item.compressPath)
watermarkPath.push(item.watermarkPath)
fileSize += parseInt(item.fileSize)
})
catalog.value.materialFilePath = materialFilePath.join(',')
catalog.value.compressPath = compressPath.join(',')
catalog.value.watermarkPath = watermarkPath.join(',')
catalog.value.fileSize = fileSize.toString()
catalog.value.fileNum = enclosureLists.value.length
catalog.value.coverpicture = enclosureLists.value[coverIndex.value].enclosureAddr
}
</script>
<style lang="scss" scoped>
.steps-container {
position: fixed;
left: 0;
right: 0;
z-index: 999;
:deep(.van-steps--horizontal) {
.van-steps__items {
padding-bottom: 32px;
.van-step__title {
font-size: 16px;
}
.van-step__circle-container {
top: 40px;
}
.van-step__line {
top: 40px;
}
}
}
}
</style>
\ No newline at end of file
<template>
<van-nav-bar title="结果" :left-arrow="true" fixed placeholder safe-area-inset-top @click-left="router.go(-1)" />
<div class="container">
<div class="info">
<img src="../../static/image/result-success.png">
<div class="title">提交成功</div>
<div class="desc">注:请前往“我的-待发布图组”进行图组发布</div>
</div>
<van-button type="primary" @click="router.pushTab({name:'mine'})">前往待发布图组</van-button>
</div>
</template>
<script setup>
const router=useRouter()
</script>
<style lang="scss" scoped>
.container {
display: flex;
flex-direction: column;
align-items: center;
gap: 30px;
.info {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
margin-top: 20vh;
.title{
font-size: 20px;
}
.desc{
color: #9f9f9f;
font-size: 16px;
}
}
.van-button{
width: 150px;
}
}
</style>
\ No newline at end of file
<template>
<div class="container">
<div class="header">
<van-search shape="round" placeholder="请输入专题关键字" />
</div>
<div class="content">
<div class="c-title">热门专题</div>
<div class="list">
<div class="item"
@click="router.push({ name: 'subject-list', params: { tagId: item.tagId, tagName: item.tagName } })"
v-for="(item, index) in dataList" :key="index">
<img class="cover" src="../../static/image/subject-cover.png">
<div class="title">{{ item.tagName }}</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { selectTagsList } from '@/api/subject'
import { useInitScroll } from '@/hooks/useScroll'
useInitScroll()
const router = useRouter()
const queryParams = reactive({
tagName: '专题',
type: 1
})
const dataList = ref([])
const getList = async () => {
const res = await selectTagsList(queryParams)
dataList.value = res.data
}
getList()
</script>
<style lang="scss" scoped>
.container {
.content {
padding: 20px 15px 0 15px;
.c-title {
font-size: 16px;
color: #232524;
}
.list {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 10px;
.item {
width: calc(50% - 12px);
text-align: center;
padding: 13px 0;
box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.1);
background-image: url('../../static/image/subject-bg.png');
background-size: cover;
background-repeat: no-repeat;
background-position: center;
.cover {
width: 77px;
height: 77px;
border-radius: 50%;
}
.title {
margin-top: 19px;
font-size: 14px;
color: #363636;
}
}
}
}
}
</style>@/hooks/useScroll
\ No newline at end of file
<template>
<div class="container">
<van-sticky>
<!-- <van-nav-bar title="专题列表" :left-arrow="true" fixed placeholder safe-area-inset-top
@click-left="router.go(-1)" /> -->
<div class="header">
<div class="h-content">
<img src="../../static/image/subject-list-logo.png" />
<div class="title">当前专题:{{ queryParams.tagName }}</div>
</div>
<div class="overlay"></div>
</div>
</van-sticky>
<div class="content">
<van-pull-refresh v-model="refreshing" @refresh="handleRefresh">
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" :offset="50"
@load="getList" v-if="dataList.length">
<div class="list">
<div class="item" v-for="(item, index) in dataList" :key="index"
@click="router.push({ name: 'home-content', params: { id: item.materialId } })">
<img :src="fileDomain + item.coverpicture">
<div class="desc">
<span class="title">
<van-text-ellipsis :content="item.materialTitle" />
</span>
<div class="tag">
<span v-for="(tagName, index) in item.tagName.split(',')" :key="index">{{
tagName }}</span>
</div>
<div class="info">
<div class="user">
<img src="../../static/image/avatar.png">
<span>{{ item.author }}</span>
</div>
<div class="collect">
<van-icon name="star-o" color="#9F9F9F" v-if="index < 5" />
<van-icon name="star" color="#F1C13E" v-else />
<span>收藏</span>
</div>
</div>
</div>
</div>
</div>
</van-list>
<van-empty description="暂无数据" v-else />
</van-pull-refresh>
</div>
</div>
</template>
<script setup name="subject-list">
import { selectSubjectList } from '@/api/subject'
import { useInitScroll } from '@/hooks/useScroll'
useInitScroll()
const router = useRouter()
const route = useRoute()
const refreshing = ref(false)
const loading = ref(false)
const finished = ref(true)
const fileDomain = import.meta.env.VITE_APP_FILE_URL
const page = reactive({
pageNum: 0,
pageSize: 10
})
const queryParams = reactive({
tagId: null,
tagName: '',
materialType: 3,
typeClassify: '',
organizeId: null,
organizeName: ''
})
onLoad((options) => {
queryParams.tagId = options.tagId
queryParams.tagName = options.tagName
getList()
})
const dataList = ref([])
const handleSelectDataList = async () => {
const res = await selectSubjectList(page, queryParams)
setDataList(res)
}
const getList = () => {
page.pageNum++
loading.value = true
handleSelectDataList()
}
const setDataList = (res) => {
if (refreshing.value) {
dataList.value = res.data.list
} else {
dataList.value.push(...res.data.list)
}
loading.value = false
finished.value = dataList.value.length >= res.data.total
refreshing.value = false
}
const handleRefresh = () => {
window.scroll(0, 0)
refreshing.value = true
page.pageNum = 0
getList()
}
// onActivated(() => {
// nextTick(() => { }).then(() => {
// if (queryParams.tagId != route.query.tagId) {
// handleRefresh()
// }
// })
// })
</script>
<style lang="scss" scoped>
.container {
background-color: #f6f6f6;
min-height: 100vh;
.header {
background-image: url('../../static/image/subject-list-bg.png');
background-size: cover;
background-repeat: no-repeat;
background-position: center;
position: relative;
.h-content {
position: relative;
z-index: 999;
width: 100%;
height: 62px;
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
img {
width: 23px;
height: 23px;
}
.title {
font-size: 15px;
color: #ffffff;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.overlay {
background-color: #000000;
opacity: 0.3;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
}
.content {
padding: 0 10px;
.list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-top: 20px;
.item {
break-inside: avoid;
background-color: #ffffff;
border-radius: 5px;
margin-bottom: 10px;
width: 49%;
img {
width: 100%;
height: 150px;
object-fit: cover;
}
.desc {
padding: 5px 10px;
display: flex;
flex-direction: column;
gap: 5px;
.title {
font-size: 16px;
}
.tag {
display: flex;
flex-wrap: wrap;
gap: 3px;
span {
font-size: 14px;
padding: 0 5px;
background-color: #F3F8FF;
color: #4C70AC;
font-size: 12PX;
border-radius: 3px;
}
}
.info {
display: flex;
justify-content: space-between;
padding: 5px 0;
.user {
display: flex;
gap: 7px;
align-items: center;
img {
width: 23px;
height: 23px;
border-radius: 50%;
}
span {
color: #9F9F9F;
font-size: 12px;
}
}
}
.collect {
display: flex;
align-items: center;
gap: 3px;
span {
color: #9F9F9F;
font-size: 12px;
}
}
}
}
}
}
}
</style>
\ No newline at end of file
import { createRouter } from 'uni-mini-router'
// 导入pages.json
import pagesJson from '../pages.json'
// 引入uni-parse-pages
import pagesJsonToRoutes from 'uni-parse-pages'
// 生成路由表
import useUserStore from '@/store/user'
const routes = pagesJsonToRoutes(pagesJson)
const router = createRouter({
routes: [...routes] // 路由表信息
})
router.beforeEach((to, from, next) => {
const userStore = useUserStore()
if(to.meta) {
if (to.meta.requireAuth) {
if (userStore.isLogin()) {
next()
} else {
next({ name: 'account-login' })
}
} else {
next()
}
} else {
next()
}
})
export default router
import request from '@/services/request';
export function getList(params) {
return request.get('/api/queryList', params);
}
import * as foo from './foo';
const api = {
foo,
};
export default api;
type RequestConfig = UniApp.RequestOptions;
type IInterceptors = {
request?: {
onFulfilled?: (config: RequestConfig) => RequestConfig;
onRejected?: (error: any) => any;
};
response?: {
onFulfilled?: (result: UniApp.RequestSuccessCallbackResult) => any;
onRejected?: (error: any) => any;
};
};
class Request {
// 拦截器
private readonly interceptors?: IInterceptors;
constructor(config: { interceptors?: IInterceptors } = {}) {
this.interceptors = config.interceptors;
}
/**
* @description 统一结果处理
*/
private resultHandle(res: Promise<any>) {
return res.then((r) => [r, null]).catch((e) => [null, e]);
}
http(_config: RequestConfig): Promise<any> {
const requestOnFulfilled = this.interceptors?.request?.onFulfilled;
const requestOnRejected = this.interceptors?.request?.onRejected;
const responseOnFulfilled = this.interceptors?.response?.onFulfilled;
const responseOnRejected = this.interceptors?.response?.onRejected;
let config: RequestConfig = { ..._config, header: _config.header || {} };
if (requestOnFulfilled) {
try {
config = requestOnFulfilled(config);
} catch (e) {
requestOnRejected?.(e);
}
}
return new Promise((resolve, reject) => {
xma.request({
...config,
success: (result) => {
if (`${result.statusCode}`.startsWith('2')) {
resolve(responseOnFulfilled?.(result) || result);
} else {
reject(responseOnRejected?.(result) || result);
}
},
fail: (error) => {
reject(responseOnRejected?.(error) || error);
},
});
});
}
post(url: string, data: any = {}, config?: RequestConfig) {
return this.resultHandle(
this.http({
method: 'POST',
url,
data,
...config,
}),
);
}
get(url: string, data: any = {}, config?: RequestConfig) {
return this.resultHandle(
this.http({
method: 'GET',
url,
data,
...config,
}),
);
}
}
const request = new Request({
interceptors: {
request: {
onFulfilled(config) {
return config;
},
},
response: {
onFulfilled(result) {
return result.data;
},
onRejected(error) {
return error;
},
},
},
});
export default request;
export {}
declare module "vue" {
type Hooks = App.AppInstance & Page.PageInstance;
interface ComponentCustomOptions extends Hooks {}
}
\ No newline at end of file
import { defineStore } from 'pinia';
import { ref } from 'vue';
type ICountStore = {
count: number;
};
const initState: ICountStore = {
count: 0,
};
export const useCountStore = defineStore('user', () => {
const countInfo = ref<ICountStore>({ ...initState });
const addCount = (count: number = 1) => {
countInfo.value.count += count;
};
return {
countInfo,
addCount,
};
});
import { createPinia } from 'pinia';
const store = createPinia();
export default store;
// 模块统一导出
export * from './count';
import { defineStore } from 'pinia'
const useUserStore = defineStore(
'user',
{
state: () => ({
userinfo: null,
token: null
}),
actions: {
login(result) {
const { loginModel, data } = result
this.userinfo = loginModel
this.token = data
},
isLogin() {
return this.userinfo && this.token
},
logout() {
this.userinfo = null
this.token = null
}
},
persist: {
enabled: true, // true 表示开启持久化保存
strategies: [
{
storage: localStorage //表示存储在localStorage
}
]
}
})
export default useUserStore
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color: #333; // 基本色
$uni-text-color-inverse: #fff; // 反色
$uni-text-color-grey: #999; // 辅助灰色,如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable: #c0c0c0;
/* 背景颜色 */
$uni-bg-color: #fff;
$uni-bg-color-grey: #f8f8f8;
$uni-bg-color-hover: #f1f1f1; // 点击状态颜色
$uni-bg-color-mask: rgba(0, 0, 0, 0.4); // 遮罩颜色
/* 边框颜色 */
$uni-border-color: #c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm: 12px;
$uni-font-size-base: 14px;
$uni-font-size-lg: 16;
/* 图片尺寸 */
$uni-img-size-sm: 20px;
$uni-img-size-base: 26px;
$uni-img-size-lg: 40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* 垂直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2c405a; // 文章标题颜色
$uni-font-size-title: 20px;
$uni-color-subtitle: #555; // 二级标题颜色
$uni-font-size-subtitle: 18px;
$uni-color-paragraph: #3f536e; // 文章段落颜色
$uni-font-size-paragraph: 15px;
\ No newline at end of file
// 页面白名单,不受拦截
const whiteList = [
'/pages/home/index',
'/pages/classify/index',
]
function hasPermission(url) {
let hasLogin = uni.getStorageSync('hasLogin'); //在这可以使用token,isLogin是登录成功后在本地存储登录标识
hasLogin = Boolean(Number(hasLogin)); //返回布尔值
// 在白名单中或有登录判断条件可以直接跳转
if (whiteList.indexOf(url) !== -1 || hasLogin) {
console.log('通过')
return true
}
console.log('失败')
return false
}
uni.addInterceptor('switchTab', {
// tabbar页面跳转前进行拦截
invoke(e) {
if (!hasPermission(e.url)) {
uni.reLaunch({
url: '/pages/account/login'
})
console.log('不在白名单内')
return false
}
console.log('在白名单内')
return true
},
success(e) {
}
})
\ No newline at end of file
import axios from 'axios'
import { showToast } from 'vant'
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: import.meta.env.VITE_APP_BASE_API,
// 超时
timeout: 100000
})
// request拦截器
service.interceptors.request.use(config => {
if (config.headers['Content-Type'] == 'application/json;charset=utf-8') {
config.data.systemCode = import.meta.env.VITE_APP_SYSTEM_CODE
if (config.requireAuth) {
const user = JSON.parse(localStorage.getItem('user'))
config.data.token = user.token
}
}
console.log('config--->',config)
return config
}, error => {
Promise.reject(error)
})
// 响应拦截器
service.interceptors.response.use(res => {
// 未设置状态码则默认成功状态
const code = res.data.code;
// 获取错误信息
const message = res.data.message
if (code !== 1 && code !== 200) {
showToast(message)
return Promise.reject(message)
} else {
return res.data
}
},
error => {
let { message, response } = error;
if (message == "Network Error") {
message = "后端接口连接异常";
}
else if (message.includes("timeout")) {
message = "系统接口请求超时";
}
else if (message.includes("Request failed with status code")) {
if (response.data.code == 401) {
errorHandlerBy401()
} else {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
}
showToast(message)
return Promise.reject(error)
}
)
export default service
{
"extends": "@vue/tsconfig/tsconfig.json",
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"noImplicitThis": true,
"allowSyntheticDefaultImports": true,
"allowJs": true,
"sourceMap": true,
"noImplicitAny": false,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"outDir": "dist",
"lib": ["esnext", "dom"],
"types": ["@dcloudio/types", "wot-design-uni/global.d.ts"]
},
"vueCompilerOptions": {
"target": 3,
"nativeTags": ["block", "template", "component", "slot"]
},
"exclude": ["node_modules"],
"include": [
"src/**/*",
"src/**/*.ts",
"src/**/*.js",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.jsx",
"src/**/*.vue",
"src/**/*.json"
],
"files": ["src/**/*", "useScroll.js"]
}
import { defineConfig, loadEnv } from 'vite';
import uni from '@dcloudio/vite-plugin-uni';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { VantResolver } from '@vant/auto-import-resolver';
import legacy from '@vitejs/plugin-legacy';
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
const { VITE_APP_ENV, VITE_APP_BASE_URL, VITE_APP_OUTDIR_FILENAME } = env
const fileName = VITE_APP_OUTDIR_FILENAME ?? 'dist'
return {
plugins: [
uni(),
AutoImport({
imports: [
'vue',
'uni-app',
'vue-router',
{
from: 'uni-mini-router',
imports: ['createRouter', 'useRouter', 'useRoute']
},
{
'rexma-cli': ['xma'],
},
],
dts: 'src/declaration/auto-imports.d.ts',
resolvers: [VantResolver()],
}),
Components({
resolvers: [VantResolver()],
}),
process.env.UNI_PLATFORM === 'h5' &&
legacy({
targets: ['defaults', 'not IE 11'],
}),
].filter(Boolean),
// base: VITE_APP_BASE_URL,
server: {
host: '0.0.0.0',
proxy: {
'/dev-api': {
target: 'https://info.gyntv.com.cn/back/ty-visit/ty-visit',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/dev-api/, ""),
bypass: (req, res, options) => res.setHeader("x-req-proxyUr1", options.target + req.url.replace('/dev-api', ''))
},
'/back/ty-visit/ty-visit': {
target: 'https://info.gyntv.com.cn',
changeOrigin: true,
bypass: (req, res, options) => res.setHeader("x-req-proxyUr1", options.target + req.url)
},
'/download': {
target: VITE_APP_ENV === 'production' ? 'https://info.gyntv.com.cn/' : 'https://info.gyntv.com.cn/',
changeOrigin: true,
bypass: (req, res, options) => res.setHeader("x-req-proxyUr1", options.target + req.url)
}
}
},
// build: {
// outDir: fileName,
// sourcemap: false,
// minify: 'terser',
// chunkSizeWarningLimit: 1500,
// terserOptions: {
// compress: {
// drop_console: true,
// drop_debugger: true
// }
// },
// }
}
});
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment