上传

parent ba1ec06e
......@@ -20,4 +20,4 @@ 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 = 'jiaxiu-centre'
\ No newline at end of file
VITE_APP_SYSTEM_CODE = 'ty-centre'
\ No newline at end of file
......@@ -9,13 +9,24 @@ declare module 'vue' {
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
VanArea: typeof import('vant/es')['Area']
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']
VanTabbar: typeof import('vant/es')['Tabbar']
......
......@@ -23,6 +23,7 @@
"@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",
......@@ -45,9 +46,12 @@
"@vitejs/plugin-legacy": "4.1.1",
"@vue/runtime-core": "^3.3.11",
"@vue/tsconfig": "^0.1.3",
"add": "^2.0.6",
"rexma-cli": "^1.8.0",
"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",
......
import request from '@/utils/request'
/**
* @description 账号密码登录
* @param {any} data 账号密码登录
* @returns
*/
export const loginByPassword = (data) => {
return request({
url: '/login',
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 {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
......@@ -8,6 +8,7 @@ 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']
......@@ -79,8 +80,8 @@ declare global {
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useLink: typeof import('vue-router')['useLink']
const useRoute: typeof import('vue-router')['useRoute']
const useRouter: typeof import('vue-router')['useRouter']
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']
......
......@@ -3,6 +3,11 @@ import App from "./App.vue";
import store from "./store";
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'
// import '@/utils/interceptor.js'
export function createApp() {
const app = createSSRApp(App);
......
......@@ -8,15 +8,88 @@
"pages": [ // pages 数组中第一项表示应用启动页
{
"path": "pages/home/index",
"name": "home",
"style": {
"navigationBarTitleText": "甲秀图库"
}
},
{
"path": "pages/classify/index",
"name": "classify",
"style": {
"navigationBarTitleText": "分类"
}
},
{
"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/account/login",
"name": "account-login",
"style": {
"navigationBarTitleText": "登录"
}
},
{
"path": "pages/account/userinfo",
"name": "account-userinfo",
"style": {
"navigationBarTitleText": "用户信息"
},
"meta": {
"requireAuth": true
}
}
],
"globalStyle": {
......@@ -35,14 +108,19 @@
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/home/index",
"iconPath": "assets/image/home.png",
"selectedIconPath": "assets/image/home-select.png",
"iconPath": "../src/assets/image/home.png",
"selectedIconPath": "../src/assets/image/home-select.png",
"text": "首页"
}, {
"pagePath": "pages/classify/index",
"iconPath": "../src/assets/image/classify.png",
"selectedIconPath": "../src/assets/image/classify-select.png",
"text": "分类"
}, {
"pagePath": "pages/mine/index",
"iconPath": "assets/image/mine.png",
"selectedIconPath": "assets/image/mine-select.png",
"iconPath": "../src/assets/image/mine.png",
"selectedIconPath": "../src/assets/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="@/assets/image/login.png"></div>
<div class="logo-imgs">
<img src="">
</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>
<button type="button" class="conf-btn login-btns" @click="login">登录</button>
</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 } 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'})
}
</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: 35%;
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;
}
.login-group_re i {
line-height: 37px;
color: #4C82FF;
font-size: 0.5rem;
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%;
}
</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>分类</div>
</template>
<script setup>
</script>
<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="../../assets/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
......@@ -38,7 +38,7 @@
v-if="dataList.length">
<div class="list">
<div class="item" v-for="(item, index) in dataList" :key="index"
@click="router.push({ name: 'home-content', query: { id: item.materialId } })">
@click="router.push({ name: 'home-content', params: { id: item.materialId } })">
<img :src="fileDomain + item.coverpicture">
<div class="desc">
<span class="title">
......@@ -186,8 +186,6 @@ const confirmTagsPopup = (e) => {
refreshing.value = true
getList()
}
</script>
<style lang="scss" scoped>
......
<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>
<img src="@/assets/image/avatar.png">
<span>{{ item.author }}</span>
</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, publishPicture } from '@/api/publish'
import { showLoadingToast, closeToast, showToast } from 'vant'
const props = defineProps({
active: {
type: Number,
default: 0
}
})
const emits = defineEmits(["total", "refresh"])
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 handlePublishPicture = async (e) => {
showLoadingToast({
message: '发布中',
forbidClick: true,
duration: 0
})
const data = {
materialId: e.materialId,
organizeId: e.organizeId,
organizeName: e.organizeName,
collect: 1
}
await publishPicture(data)
closeToast()
showToast('发布成功')
handleRefresh()
emits('refresh')
}
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;
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>
<div>
<h1>我的</h1>
<div class="container" v-if="userStore.isLogin">
<div class="userinfo">
<img class="avatar"
mode="aspectFill"
:src="fileDomain + userStore.userinfo.user.headSculpturePhoto ?? '../../assets/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="../../assets/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="content">
<van-tabs v-model:active="active" background="#f8f8f8" animated swipeable sticky>
<van-tab title="待发布">
<picture-list :active="0" @total="setTotal" @refresh="handleRefreshPublishPicture"></picture-list>
</van-tab>
<van-tab title="已发布">
<picture-list ref="publishPicture" :active="1" @total="setTotal"></picture-list>
</van-tab>
<van-tab title="收藏">
<picture-list :active="2" @total="setTotal"></picture-list>
</van-tab>
</van-tabs>
</div>
</div>
</template>
<script setup>
import { showConfirmDialog, showToast } from 'vant'
import useUserStore from '@/store/user'
import PictureList from './components/PictureList'
import { useInitScroll } from '@/hooks/useScroll'
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 logout = () => {
showConfirmDialog({
title: '提示',
message:
'确定退出登录吗',
}).then(() => {
userStore.logout()
showToast('退出登录成功')
router.push({ name: 'account-login' })
}).catch(() => {
})
}
</script>
<style scoped>
</style>
\ No newline at end of file
<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;
}
}
.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="../../../assets/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 '@/stores/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
This diff is collapsed.
<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.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')
}
</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="@/assets/image/close.png" @click="handleDelete(index)">
</div>
</div>
<div class="info">
<span class="title">图片{{ index + 1 }}描述:</span>
<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 = () => {
console.log(inputRef)
inputRef.value.click()
}
const handleDelete=(index)=>{
fileList.value.splice(index,1)
}
const changeFile = (e) => {
const files = e.target.files
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 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 })
}
</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%;
position: relative;
img {
width: 100%;
height: 100px;
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;
.title {
color: #545454;
font-size: 15px;
}
textarea {
border: 1px solid #cccccc;
flex: 1;
border-radius: 5px;
font-size: 14px;
padding: 10px;
}
}
}
}
.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 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
}
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[0].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="../../assets/image/result-success.png">
<div class="title">提交成功</div>
<div class="desc">注:请前往“我的-待发布图组”进行图组发布</div>
</div>
<van-button type="primary" @click="router.push({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', query: { tagId: item.tagId, tagName: item.tagName } })"
v-for="(item, index) in dataList" :key="index">
<img class="cover" src="../../assets/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('../../assets/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="../../assets/image/subject-list-logo.png" />
<div class="title">当前专题:{{ route.query.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', query: { 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="../../assets/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: ''
})
const dataList = ref([])
const handleSelectDataList = async () => {
queryParams.tagId = route.query.tagId
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('../../assets/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
<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="../../../assets/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 '@/stores/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
This diff is collapsed.
<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="../../../assets/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 '@/stores/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
This diff is collapsed.
import { createRouter, createWebHistory } from 'vue-router'
// import useUserStore from '@/stores/user'
// import useCoreStore from '@/stores/core'
import Index from '../pages/index'
import HomeIndex from '../pages/home/index'
// import HomeContent from '../views/home/content'
// import PublishIndex from '../views/publish/index'
// import PublishResult from '../views/publish/result'
// import MineIndex from '../views/mine/index'
// import AccountLogin from '../views/account/login'
// import AccountUserinfo from '../views/account/userinfo'
// import SubjectIndex from '../views/subject/index'
// import SubjectList from '../views/subject/list'
// import PrefectureIndex from '../views/prefecture/index'
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({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'index',
component: Index,
redirect: '/home',
meta: {
keepAlive: true
},
children: [{
path: '/home',
name: 'home',
component: HomeIndex
},
// {
// path: '/mine',
// name: 'mine',
// component: MineIndex,
// meta: {
// requireAuth: true
// }
// },
// {
// path: '/prefecture',
// name: 'prefecture',
// component: PrefectureIndex,
// meta: {
// requireAuth: true
// }
// },
// {
// path: '/subject',
// name: 'subject',
// component: SubjectIndex,
// meta: {
// requireAuth: true
// }
// }
]
},
// {
// path: '/home/content',
// name: 'home-content',
// component: HomeContent
// },
// {
// path: '/publish',
// name: 'publish',
// component: PublishIndex,
// meta: {
// requireAuth: true
// }
// },
// {
// path: '/publish/result',
// name: 'publish-result',
// component: PublishResult,
// meta: {
// requireAuth: true
// }
// },
// {
// path: '/account/login',
// name: 'account-login',
// component: AccountLogin
// },
// {
// path: '/account/userinfo',
// name: 'account-userinfo',
// component: AccountUserinfo,
// meta: {
// requireAuth: true
// }
// },
// {
// path: '/subject/list',
// name: 'subject-list',
// component: SubjectList,
// meta: {
// requireAuth: true,
// keepAlive: true
// }
// }
]
routes: [...routes] // 路由表信息
})
// router.beforeEach((to, from, next) => {
// const coreStore = useCoreStore()
// const userStore = useUserStore()
// if (to.meta.keepAlive) {
// coreStore.setCacheRouteNames(to.matched[0].name)
// }
// if (to.meta.requireAuth) {
// if (userStore.isLogin()) {
// next()
// } else {
// next({ name: 'account-login' })
// }
// } else {
// next()
// }
// })
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 { 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
// 页面白名单,不受拦截
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
......@@ -11,7 +11,7 @@ import legacy from '@vitejs/plugin-legacy';
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'
const fileName = VITE_APP_OUTDIR_FILENAME ?? 'dist'
return {
plugins: [
uni(),
......@@ -20,11 +20,15 @@ export default defineConfig(({ mode }) => {
'vue',
'uni-app',
'vue-router',
{
from: 'uni-mini-router',
imports: ['createRouter', 'useRouter', 'useRoute']
},
{
'rexma-cli': ['xma'],
},
],
dts: 'src/declaration/auto-imports.d.ts',
dts: 'src/declaration/auto-imports.d.ts',
resolvers: [VantResolver()],
}),
Components({
......@@ -35,7 +39,7 @@ export default defineConfig(({ mode }) => {
targets: ['defaults', 'not IE 11'],
}),
].filter(Boolean),
// base: VITE_APP_BASE_URL,
server: {
host: '0.0.0.0',
proxy: {
......@@ -51,7 +55,7 @@ export default defineConfig(({ mode }) => {
bypass: (req, res, options) => res.setHeader("x-req-proxyUr1", options.target + req.url)
},
'/download': {
target: VITE_APP_ENV === 'production' ? 'https://info.gyntv.com.cn/' : 'http://192.168.98.43:88',
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)
}
......
......@@ -2050,6 +2050,11 @@
dependencies:
undici-types "~5.26.4"
"@vant/area-data@^1.5.1":
version "1.5.1"
resolved "https://registry.npmmirror.com/@vant/area-data/-/area-data-1.5.1.tgz#bdd943b9b569476cb04133a8323bd04aa35be2c2"
integrity sha512-gR5TPEzTbxN1cTK1aDhCoyikSCLX7DAacxyXoKyI4SAsYYTZrDl/nLgQFIm9vLsvWzlPIda8xV8/U3x7M9k6ww==
"@vant/auto-import-resolver@^1.2.1":
version "1.2.1"
resolved "https://registry.npmmirror.com/@vant/auto-import-resolver/-/auto-import-resolver-1.2.1.tgz#5848d6ffb5876c9c1dd2d1ec06d062ef45b57844"
......@@ -2336,6 +2341,11 @@ acorn@^8.11.3, acorn@^8.8.2:
resolved "https://registry.npmmirror.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c"
integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==
add@^2.0.6:
version "2.0.6"
resolved "https://registry.npmmirror.com/add/-/add-2.0.6.tgz#248f0a9f6e5a528ef2295dbeec30532130ae2235"
integrity sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q==
address@^1.1.2:
version "1.2.2"
resolved "https://registry.npmmirror.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e"
......@@ -4431,6 +4441,16 @@ undici-types@~5.26.4:
resolved "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
uni-mini-router@^0.1.6:
version "0.1.6"
resolved "https://registry.npmmirror.com/uni-mini-router/-/uni-mini-router-0.1.6.tgz#367cbc0eec6e063d6c65bf2a227bbb8dcee5d2a9"
integrity sha512-dgeEYCv00yTkiX3gVAQXrx4IAzAVtZv+2lVtVaixH6J/IHfZP7R3wqUwBvy4eFD0vP20p+QWNNE2c5LuHbPSiQ==
uni-parse-pages@^0.0.1:
version "0.0.1"
resolved "https://registry.npmmirror.com/uni-parse-pages/-/uni-parse-pages-0.0.1.tgz#da3ee32894a714bc6ae05676fc52093f301e49a0"
integrity sha512-OyuNFo/nCw7mnZnrtTYi/C12uWxmK7FczrxRATZETCh7j23uk1kOxEWVD4BXRSjif7YJg0ZPYQE4xBWqxUMHAw==
unicode-canonical-property-names-ecmascript@^2.0.0:
version "2.0.0"
resolved "https://registry.npmmirror.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
......
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