feat: upload cover not over yet
@@ -3,7 +3,7 @@ export default defineAppConfig({
|
||||
'pages/login/index/index',
|
||||
'pages/login/verification/index',
|
||||
'pages/login/terms/index',
|
||||
// 'pages/publishBall/index',
|
||||
'pages/publishBall/index',
|
||||
// 'pages/mapDisplay/index',
|
||||
// 'pages/list/index',
|
||||
'pages/index/index',
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
@use '~@/scss/images.scss' as img;
|
||||
@use '~@/scss/themeColor.scss' as theme;
|
||||
|
||||
.upload-cover-root {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 112px;
|
||||
margin-bottom: 8px;
|
||||
position: relative;
|
||||
align-items: flex-end;
|
||||
|
||||
&.upload-cover-act-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.upload-cover-act {
|
||||
display: flex;
|
||||
width: 108px;
|
||||
height: 108px;
|
||||
padding: 16px 12px 10px 12px;
|
||||
margin-top: 4px;
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-radius: 20px;
|
||||
border: 1px dashed rgba(0, 0, 0, 0.12);
|
||||
background: theme.$page-background-color;
|
||||
box-shadow: 0 4px 36px 0 rgba(0, 0, 0, 0.06);
|
||||
z-index: 1;
|
||||
|
||||
.upload-cover-act-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.upload-cover-text {
|
||||
color: var(--Labels-Secondary, var(--Labels-Secondary, rgba(60, 60, 67, 0.60)));
|
||||
font-feature-settings: 'liga' off, 'clig' off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px; /* 166.667% */
|
||||
}
|
||||
}
|
||||
|
||||
.cover-image-list-container {
|
||||
position: absolute;
|
||||
left: 114px;
|
||||
top: 0;
|
||||
width: calc(100% - 114px);
|
||||
overflow-x: scroll;
|
||||
height: 112px;
|
||||
|
||||
&.full {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cover-image-list {
|
||||
width: auto;
|
||||
height: 112px;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-end;
|
||||
flex-wrap: nowrap;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
.cover-image-item {
|
||||
display: flex;
|
||||
width: 108px;
|
||||
height: 108px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-radius: 20px;
|
||||
position: relative;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
box-sizing: border-box;
|
||||
|
||||
.cover-image-item-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.cover-image-item-delete {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
right: -4px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-source-popup-container {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 26px 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.upload-source-popup-item {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
padding: 16px 24px;
|
||||
box-sizing: border-box;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,128 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Popup } from "@nutui/nutui-react-taro";
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { Image, View, Text } from '@tarojs/components'
|
||||
import img from '../../config/images'
|
||||
import UploadSourcePopup from './upload-source-popup'
|
||||
import UploadFromWx from './upload-from-wx'
|
||||
import { CommonPopup } from '../'
|
||||
|
||||
import './index.scss'
|
||||
import { uploadFileResponseData } from '@/services/uploadFiles'
|
||||
|
||||
export default function UploadCover(props) {
|
||||
const { value = [], onChange = () => {} } = props
|
||||
export type sourceType = 'album' | 'history' | 'preset'
|
||||
|
||||
export type source = sourceType[]
|
||||
|
||||
export type CoverImageValue = {
|
||||
id: string
|
||||
url: string
|
||||
tempFilePath?: string
|
||||
}
|
||||
|
||||
export interface UploadCoverProps {
|
||||
value: CoverImageValue[]
|
||||
onChange: (value: CoverImageValue[] | ((prev: CoverImageValue[]) => CoverImageValue[])
|
||||
) => void
|
||||
source: source
|
||||
maxCount: number
|
||||
}
|
||||
|
||||
// const values = [
|
||||
// 'http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/1a35ebbf-2361-44da-b338-7608561d0b31.png',
|
||||
// 'http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/cf5a82ba-90af-4138-a1b3-9119adcde9e0.png',
|
||||
// 'http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/49d7cdf0-b03c-4a0f-91c6-e7778080cfcd.png'
|
||||
// ]
|
||||
|
||||
const mergeCoverImages = (value: CoverImageValue[], images: CoverImageValue[]) => {
|
||||
console.log(value, images, 11111)
|
||||
// 根据id来更新url, 如果id不存在,则添加到value中
|
||||
const newImages = images
|
||||
const updatedValue = value.map(item => {
|
||||
const index = images.findIndex(image => image.id === item.id)
|
||||
if (index !== -1) {
|
||||
newImages.splice(index, 1)
|
||||
return { ...item, url: images[index].url }
|
||||
}
|
||||
return item
|
||||
})
|
||||
return [...updatedValue, ...newImages]
|
||||
}
|
||||
|
||||
export default function UploadCover(props: UploadCoverProps) {
|
||||
const {
|
||||
value = [],
|
||||
onChange = () => void 0,
|
||||
source = ['album', 'history', 'preset'],
|
||||
maxCount = 9,
|
||||
} = props
|
||||
const [visible, setVisible] = useState(false)
|
||||
|
||||
const onAdd = useCallback((images: CoverImageValue[]) => {
|
||||
onChange(prev => mergeCoverImages(prev, images))
|
||||
setVisible(false)
|
||||
}, [value])
|
||||
|
||||
const onWxAdd = useCallback((images: CoverImageValue[], onFileUploaded: Promise<{ id: string, data: uploadFileResponseData }[]>) => {
|
||||
onAdd(images)
|
||||
onFileUploaded.then(res => {
|
||||
console.log(res, 11111)
|
||||
onAdd(res.map(item => ({
|
||||
id: item.id,
|
||||
url: item.data.file_path,
|
||||
})))
|
||||
})
|
||||
}, [onAdd])
|
||||
const onDelete = (image: CoverImageValue) => {
|
||||
onChange(value.filter(item => item.id !== image.id))
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Popup
|
||||
<CommonPopup
|
||||
visible={visible}
|
||||
onClose={() => setVisible(false)}
|
||||
round
|
||||
closeable
|
||||
position="bottom"
|
||||
hideFooter
|
||||
>
|
||||
<div className="upload-cover-popup" onClick={}>
|
||||
<div className="upload-cover-popup-title">上传封面</div>
|
||||
<View className="upload-source-popup-container" style={{ height: source.length * 56 + 52 + 'px' }}>
|
||||
{
|
||||
source.map((item) => {
|
||||
return (
|
||||
<View className="upload-source-popup-item" key={item}>
|
||||
{
|
||||
item === 'album' ? (
|
||||
<UploadFromWx onAdd={onWxAdd} maxCount={maxCount - value.length} />
|
||||
) : (
|
||||
<UploadSourcePopup sourceType={item} onAdd={onAdd} maxCount={maxCount - value.length} />
|
||||
)
|
||||
}
|
||||
</View>
|
||||
)
|
||||
})
|
||||
}
|
||||
</View>
|
||||
</CommonPopup>
|
||||
<div className={`upload-cover-root ${value.length === 0 ? 'upload-cover-act-center' : ''}`}>
|
||||
{value.length < maxCount && (
|
||||
<div className="upload-cover-act" onClick={() => setVisible(true)}>
|
||||
<Image className='upload-cover-act-icon' src={img.ICON_ADD} />
|
||||
<div className="upload-cover-text">添加活动封面</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={`cover-image-list-container ${value.length === maxCount ? 'full' : ''}`}>
|
||||
<div className="cover-image-list">
|
||||
{
|
||||
value.map((item) => {
|
||||
return (
|
||||
<View className="cover-image-item" key={item.id}>
|
||||
<Image className="cover-image-item-image" src={item.url} />
|
||||
<Image className="cover-image-item-delete" src={img.ICON_REMOVE} onClick={() => onDelete(item)} />
|
||||
</View>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
11
src/components/UploadCover/upload-from-wx.scss
Normal file
@@ -0,0 +1,11 @@
|
||||
.upload-from-wx-text {
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
padding: 16px 24px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
47
src/components/UploadCover/upload-from-wx.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import React from 'react'
|
||||
import { View, Text } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import uploadApi from '@/services/uploadFiles'
|
||||
import './upload-from-wx.scss'
|
||||
import { CoverImageValue } from '.'
|
||||
import { uploadFileResponseData } from '@/services/uploadFiles'
|
||||
|
||||
export interface UploadFromWxProps {
|
||||
onAdd: (images: CoverImageValue[], onFileUploaded: Promise<{ id: string, data: uploadFileResponseData }[]>) => void
|
||||
maxCount: number
|
||||
}
|
||||
|
||||
export default function UploadFromWx(props: UploadFromWxProps) {
|
||||
const {
|
||||
onAdd = () => void 0,
|
||||
maxCount = 9, // calc from parent
|
||||
} = props
|
||||
const handleImportFromWx = () => {
|
||||
Taro.chooseImage({
|
||||
count: maxCount,
|
||||
sizeType: ['original', 'compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
}).then(async (res) => {
|
||||
// TODO: compress image
|
||||
// TODO: cropping image to const size
|
||||
let count = 0
|
||||
const files = res.tempFiles.map(item => ({
|
||||
filePath: item.path,
|
||||
description: 'test',
|
||||
tags: 'test',
|
||||
is_public: 1 as unknown as 0 | 1,
|
||||
id: (Date.now() + count++).toString(),
|
||||
}))
|
||||
const onFileUploaded = uploadApi.batchUpload(files)
|
||||
onAdd(res.tempFiles.map(item => ({
|
||||
id: Date.now().toString(),
|
||||
url: item.path,
|
||||
})), onFileUploaded) // TODO: add loading state
|
||||
})
|
||||
}
|
||||
return (
|
||||
<View onClick={handleImportFromWx}>
|
||||
<Text className="upload-from-wx-text">从相册添加</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
156
src/components/UploadCover/upload-source-popup.scss
Normal file
@@ -0,0 +1,156 @@
|
||||
@use '~@/scss/themeColor.scss' as theme;
|
||||
|
||||
.upload-source-popup-text {
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
padding: 16px 24px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.upload-popup {
|
||||
width: 100%;
|
||||
padding: 26px 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
.upload-popup-title {
|
||||
display: flex;
|
||||
padding: 18px 20px 10px 20px;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px; /* 150% */
|
||||
letter-spacing: -0.23px;
|
||||
}
|
||||
|
||||
.upload-popup-scroll-view {
|
||||
max-height: calc(100vh - 260px);
|
||||
overflow-y: auto;
|
||||
|
||||
.upload-popup-image-list {
|
||||
width: 100%;
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 10px 10px;
|
||||
|
||||
.upload-popup-image-item {
|
||||
aspect-ratio: 1/1;
|
||||
border-radius: 9px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
box-sizing: border-box;
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
margin: 0;
|
||||
position: relative;
|
||||
|
||||
&:not(.selected) {
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-popup-image-item-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 9px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.upload-popup-image-item-select {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&.selected {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
|
||||
.select-image-icon {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-image-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-popup-image-list-empty {
|
||||
width: 100%;
|
||||
height: 60vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.upload-popup-image-list-empty-image {
|
||||
width: 80%;
|
||||
aspect-ratio: 4/3;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.upload-popup-image-list-empty-text {
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px; /* 150% */
|
||||
letter-spacing: -0.23px;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-popup-footer {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 62px;
|
||||
padding: 8px 10px 10px 10px;
|
||||
box-sizing: border-box;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
|
||||
.upload-popup-footer-cancel, .upload-popup-footer-confirm {
|
||||
font-feature-settings: 'liga' off, 'clig' off;
|
||||
font-family: "PingFang SC";
|
||||
box-sizing: border-box;
|
||||
height: 44px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.upload-popup-footer-cancel {
|
||||
background: theme.$page-background-color;
|
||||
}
|
||||
|
||||
.upload-popup-footer-confirm {
|
||||
background: theme.$primary-color;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
|
||||
&.active {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
161
src/components/UploadCover/upload-source-popup.tsx
Normal file
@@ -0,0 +1,161 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { Image, View, Text, ScrollView, Button } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import img from '../../config/images'
|
||||
import publishService from '../../services/publishService'
|
||||
import { CommonPopup } from '../'
|
||||
import emptyStatus from '../../static/emptyStatus/publish-empty.png'
|
||||
|
||||
import './upload-source-popup.scss'
|
||||
|
||||
type SourceType = 'history' | 'preset'
|
||||
|
||||
type ImageItem = {
|
||||
id: string
|
||||
url: string
|
||||
tempFilePath?: string
|
||||
}
|
||||
|
||||
interface UploadImageProps {
|
||||
sourceType: SourceType
|
||||
onAdd: (images: ImageItem[]) => void
|
||||
maxCount: number
|
||||
}
|
||||
|
||||
const sourceMap = new Map<SourceType, string>([
|
||||
['history', '历史图库'],
|
||||
['preset', '预设图库']
|
||||
])
|
||||
|
||||
const checkImageSelected = (images: ImageItem[], image: ImageItem) => {
|
||||
return images.some(item => item.id === image.id)
|
||||
}
|
||||
|
||||
export default function UploadImage(props: UploadImageProps) {
|
||||
const {
|
||||
sourceType = 'history',
|
||||
onAdd = () => void 0,
|
||||
maxCount = 9,
|
||||
} = props
|
||||
const [visible, setVisible] = useState(false)
|
||||
const [images, setImages] = useState<ImageItem[]>([])
|
||||
const [selectedImages, setSelectedImages] = useState<ImageItem[]>([])
|
||||
|
||||
const handleImageClick = (image: ImageItem) => {
|
||||
if (checkImageSelected(selectedImages, image)) {
|
||||
setSelectedImages(selectedImages.filter(item => item.id !== image.id))
|
||||
} else if (!outOfMax) {
|
||||
setSelectedImages([...selectedImages, image])
|
||||
} else {
|
||||
Taro.showToast({
|
||||
title: `最多选择${maxCount}张图片`,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
publishService.getPictures({
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 100,
|
||||
},
|
||||
seachOption: {
|
||||
tag: '',
|
||||
resource_type: 'image',
|
||||
dateRange: [],
|
||||
},
|
||||
}).then(res => {
|
||||
if (res.success) {
|
||||
setImages(res.data.data.rows.map(item => ({
|
||||
id: Date.now().toString(),
|
||||
url: item.thumbnail_url,
|
||||
})))
|
||||
} else {
|
||||
// TODO: 显示错误信息
|
||||
Taro.showToast({
|
||||
title: res.message,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
setSelectedImages([])
|
||||
}
|
||||
}, [visible])
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (selectedImages.length > 0) {
|
||||
onAdd(selectedImages)
|
||||
setVisible(false)
|
||||
} else {
|
||||
Taro.showToast({
|
||||
title: '请选择图片',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const outOfMax = selectedImages.length >= maxCount
|
||||
|
||||
return (
|
||||
<>
|
||||
<CommonPopup
|
||||
visible={visible}
|
||||
onClose={() => setVisible(false)}
|
||||
round
|
||||
hideFooter
|
||||
position='bottom'
|
||||
>
|
||||
<View className="upload-popup">
|
||||
<View className="upload-popup-title">{sourceMap.get(sourceType)}</View>
|
||||
{/* TODO: 分页 加载更多 */}
|
||||
{/* TODO: 图片加载失败 */}
|
||||
{/* TODO: 图片加载中 */}
|
||||
<ScrollView
|
||||
scrollY
|
||||
className="upload-popup-scroll-view"
|
||||
>
|
||||
{images.length > 0 ? (
|
||||
<View className="upload-popup-image-list">
|
||||
{images.map(item => {
|
||||
const isSelected = checkImageSelected(selectedImages, item)
|
||||
return (
|
||||
<View className={`upload-popup-image-item ${outOfMax ? 'disabled' : ''} ${isSelected ? 'selected' : ''}`} onClick={() => handleImageClick(item)}>
|
||||
<Image className="upload-popup-image-item-image" src={item.url} />
|
||||
<View className={`upload-popup-image-item-select ${isSelected ? 'selected' : ''}`}>
|
||||
{isSelected ? (
|
||||
<Image className="select-image-icon" src={img.ICON_CIRCLE_SELECT_ARROW} />
|
||||
) : (
|
||||
<Image className="select-image-icon" src={img.ICON_CIRCLE_UNSELECT} />
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
) : (
|
||||
<View className="upload-popup-image-list-empty">
|
||||
<Image className="upload-popup-image-list-empty-image" src={emptyStatus} />
|
||||
<Text className="upload-popup-image-list-empty-text">暂无内容</Text>
|
||||
</View>
|
||||
)}
|
||||
</ScrollView>
|
||||
{images.length > 0 ? (
|
||||
<View className="upload-popup-footer">
|
||||
<Button className="upload-popup-footer-cancel" onClick={() => setVisible(false)}>取消</Button>
|
||||
<Button className={`upload-popup-footer-confirm ${selectedImages.length > 0 ? 'active' : ''}`} type='primary' onClick={handleConfirm}>完成</Button>
|
||||
</View>
|
||||
) : (
|
||||
<View className="upload-popup-footer">
|
||||
<Button className="upload-popup-footer-cancel" onClick={() => setVisible(false)}>取消</Button>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</CommonPopup>
|
||||
<View className="upload-source-popup-text" onClick={() => setVisible(true)}>{sourceMap.get(sourceType)}选取</View>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,19 +9,21 @@ import { SelectStadium, StadiumDetail } from './SelectStadium'
|
||||
import TimeSelector from './TimeSelector'
|
||||
import TitleInput from './TitleInput'
|
||||
import CommonPopup from './CommonPopup'
|
||||
import UploadCover from './UploadCover'
|
||||
|
||||
export {
|
||||
ActivityTypeSwitch,
|
||||
TextareaTag,
|
||||
export {
|
||||
ActivityTypeSwitch,
|
||||
TextareaTag,
|
||||
FormSwitch,
|
||||
ImageUpload,
|
||||
ImageUpload,
|
||||
FormBasicInfo,
|
||||
Range,
|
||||
NumberInterval,
|
||||
SelectStadium,
|
||||
TimeSelector,
|
||||
Range,
|
||||
NumberInterval,
|
||||
SelectStadium,
|
||||
TimeSelector,
|
||||
TitleInput,
|
||||
StadiumDetail,
|
||||
CommonPopup
|
||||
CommonPopup,
|
||||
UploadCover
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ export default {
|
||||
ICON_COST: require('@/static/publishBall/icon-cost.svg'),
|
||||
ICON_TIPS: require('@/static/publishBall/icon-tips.svg'),
|
||||
ICON_ARROW_RIGHT: require('@/static/publishBall/icon-arrow-right.svg'),
|
||||
ICON_ARROW_LEFT: require('@/static/publishBall/icon-arrow-left.svg'),
|
||||
ICON_LOGO_GO: require('@/static/publishBall/icon-logo-go.svg'),
|
||||
ICON_ARROW_LEFT: require('@/static/detail/icon-arrow-left.svg'),
|
||||
ICON_LOGO_GO: require('@/static/detail/icon-logo-go.svg'),
|
||||
ICON_SEARCH: require('@/static/publishBall/icon-search.svg'),
|
||||
ICON_MAP: require('@/static/publishBall/icon-map.svg'),
|
||||
ICON_STADIUM: require('@/static/publishBall/icon-stadium.svg'),
|
||||
@@ -18,5 +18,8 @@ export default {
|
||||
ICON_HEART_CIRCLE: require('@/static/publishBall/icon-heartcircle.png'),
|
||||
ICON_ADD: require('@/static/publishBall/icon-add.svg'),
|
||||
ICON_COPY: require('@/static/publishBall/icon-arrow-right.svg'),
|
||||
ICON_DELETE: require('@/static/publishBall/icon-delete.svg')
|
||||
ICON_DELETE: require('@/static/publishBall/icon-delete.svg'),
|
||||
ICON_CIRCLE_UNSELECT: require('@/static/publishBall/icon-circle-unselect.svg'),
|
||||
ICON_CIRCLE_SELECT: require('@/static/publishBall/icon-circle-select-ring.svg'),
|
||||
ICON_CIRCLE_SELECT_ARROW: require('@/static/publishBall/icon-circle-select-arrow.svg'),
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
@use '~@/scss/images.scss' as img;
|
||||
|
||||
.detail-page {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -24,6 +26,10 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.detail-navigator-back {
|
||||
border-right: 1px solid #444;
|
||||
}
|
||||
|
||||
.detail-navigator-back, .detail-navigator-icon {
|
||||
height: 20px;
|
||||
width: 50%;
|
||||
@@ -31,7 +37,13 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
& > svg {
|
||||
& > .detail-navigator-back-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
& > .detail-navigator-logo-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: #fff;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
useUserStats,
|
||||
useUserActions
|
||||
} from '../../store/userStore'
|
||||
import img from '../../config/images'
|
||||
import { getTextColorOnImage } from '../../utils/processImage'
|
||||
import './index.scss'
|
||||
|
||||
@@ -66,18 +67,10 @@ function Index() {
|
||||
<view className="custom-navbar">
|
||||
<View className='detail-navigator'>
|
||||
<View className='detail-navigator-back'>
|
||||
<svg width="8" height="14" viewBox="0 0 8 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7 1L1 7L7 13" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
||||
<Image className='detail-navigator-back-icon' src={img.ICON_ARROW_LEFT} />
|
||||
</View>
|
||||
<View className='detail-navigator-icon'>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.9106 10.4998L16.7487 6.92161C16.7208 6.30199 17.1953 5.79401 17.8708 5.79401C18.5462 5.79401 19.0319 6.30199 18.9984 6.92161L18.8197 10.4998C18.7862 11.0413 18.3787 11.3985 17.8652 11.3985C17.3404 11.3985 16.9329 11.019 16.9106 10.4998ZM16.7599 12.822C16.7599 12.2135 17.2623 11.8395 17.8763 11.8395C18.4848 11.8395 18.9816 12.2135 18.9816 12.822C18.9816 13.4361 18.4848 13.8101 17.8763 13.8101C17.2623 13.8101 16.7599 13.4361 16.7599 12.822Z" fill="white"/>
|
||||
<path d="M8.65674 10.3268V9.50059C8.65674 7.27329 9.93506 5.93914 12.1512 5.93914C14.3673 5.93914 15.6401 7.27887 15.6401 9.50059V10.3268C15.6401 12.5373 14.3673 13.8603 12.1512 13.8603C9.93506 13.8603 8.65674 12.5317 8.65674 10.3268ZM10.8617 9.48384V10.3212C10.8617 11.4432 11.3362 12.0907 12.1512 12.0907C12.9662 12.0907 13.4351 11.4432 13.4351 10.3212V9.48384C13.4351 8.36182 12.9662 7.7087 12.1512 7.7087C11.3362 7.7087 10.8617 8.36182 10.8617 9.48384Z" fill="white"/>
|
||||
<path d="M1 10.3323V9.41127C1 7.29562 2.32857 5.93914 4.45539 5.93914C5.2983 5.93914 6.07981 6.16243 6.6492 6.52527C7.20742 6.87137 7.56468 7.34586 7.56468 7.83151C7.56468 8.34507 7.20184 8.70233 6.68269 8.70233C6.4594 8.70233 6.27519 8.63535 6.1133 8.5237C5.66115 8.22226 5.30388 7.78127 4.62844 7.78127C3.67388 7.78127 3.20497 8.36182 3.20497 9.5285V10.3268C3.20497 11.4767 3.67388 12.0852 4.57261 12.0852C5.24248 12.0852 5.69464 11.6944 5.69464 11.1306V10.9743H5.35412C4.80148 10.9743 4.50005 10.7063 4.50005 10.2207C4.50005 9.73504 4.7959 9.48384 5.35412 9.48384H6.56546C7.40279 9.48384 7.76564 9.83552 7.76564 10.6338V10.9352C7.76564 12.7048 6.47057 13.8603 4.47772 13.8603C2.28949 13.8603 1 12.5373 1 10.3323Z" fill="white"/>
|
||||
</svg>
|
||||
|
||||
<Image className='detail-navigator-logo-icon' src={img.ICON_LOGO_GO} />
|
||||
</View>
|
||||
</View>
|
||||
{/* <view className="navbar-title">我的自定义标题</view> */}
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState } from 'react'
|
||||
import { View, Text } from '@tarojs/components'
|
||||
|
||||
import Taro from '@tarojs/taro'
|
||||
import { ImageUpload, Range, TimeSelector, TextareaTag, SelectStadium, NumberInterval, TitleInput, FormBasicInfo, FormSwitch } from '../../components'
|
||||
import { ImageUpload, Range, TimeSelector, TextareaTag, SelectStadium, NumberInterval, TitleInput, FormBasicInfo, FormSwitch, UploadCover } from '../../components'
|
||||
import { type Stadium, type CoverImage } from '../../components/index.types'
|
||||
import { FormFieldConfig, FieldType } from '../../config/formSchema/publishBallFormSchema'
|
||||
import { PublishBallFormData } from '../../../types/publishBall';
|
||||
@@ -21,17 +21,21 @@ const componentMap = {
|
||||
[FieldType.CHECKBOX]: FormSwitch,
|
||||
}
|
||||
|
||||
const PublishForm: React.FC<{
|
||||
formData: PublishBallFormData,
|
||||
onChange: (key: keyof PublishBallFormData, value: any, index?: number) => void,
|
||||
const PublishForm: React.FC<{
|
||||
formData: PublishBallFormData,
|
||||
onChange: (key: keyof PublishBallFormData, value: any, index?: number) => void,
|
||||
optionsConfig: FormFieldConfig[] }> = ({ formData, onChange, optionsConfig }) => {
|
||||
const [coverImages, setCoverImages] = useState<CoverImage[]>([])
|
||||
const [showStadiumSelector, setShowStadiumSelector] = useState(false)
|
||||
const [selectedStadium, setSelectedStadium] = useState<Stadium | null>(null)
|
||||
|
||||
// 处理封面图片变化
|
||||
const handleCoverImagesChange = (images: CoverImage[]) => {
|
||||
setCoverImages(images)
|
||||
const handleCoverImagesChange = (fn: (images: CoverImage[]) => CoverImage[]) => {
|
||||
if (fn instanceof Function) {
|
||||
setCoverImages(fn(coverImages))
|
||||
} else {
|
||||
setCoverImages(fn)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新表单数据
|
||||
@@ -80,7 +84,7 @@ const PublishForm: React.FC<{
|
||||
|
||||
// TODO: 实现提交逻辑
|
||||
console.log('提交数据:', { coverImages, formData })
|
||||
|
||||
|
||||
Taro.showToast({
|
||||
title: '发布成功',
|
||||
icon: 'success'
|
||||
@@ -102,8 +106,8 @@ const PublishForm: React.FC<{
|
||||
console.log(optionProps, item.label, formData[item.key]);
|
||||
if (item.type === FieldType.UPLOADIMAGE) {
|
||||
/* 活动封面 */
|
||||
return <ImageUpload
|
||||
images={coverImages}
|
||||
return <UploadCover
|
||||
value={coverImages}
|
||||
onChange={handleCoverImagesChange}
|
||||
{...item.props}
|
||||
/>
|
||||
@@ -149,7 +153,7 @@ const PublishForm: React.FC<{
|
||||
value={formData[item.key]}
|
||||
onChange={(value) => updateFormData(item.key as keyof PublishBallFormData, value)}
|
||||
{...optionProps}
|
||||
placeholder={item.placeholder}
|
||||
placeholder={item.placeholder}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -16,7 +16,20 @@ $-images: (
|
||||
'icon-personal': '/publishBall/icon-personal.svg',
|
||||
'icon-changda': '/publishBall/icon-changda.svg',
|
||||
'icon-cost': '/publishBall/icon-cost.svg',
|
||||
'icon-remove': '/publishBall/icon-remove.svg'
|
||||
'icon-remove': '/publishBall/icon-remove.svg',
|
||||
'icon-arrow-left': '/detail/icon-arrow-left.svg',
|
||||
'icon-logo-go': '/detail/icon-logo-go.svg',
|
||||
'icon-search': '/publishBall/icon-search.svg',
|
||||
'icon-map': '/publishBall/icon-map.svg',
|
||||
'icon-stadium': '/publishBall/icon-stadium.svg',
|
||||
'icon-arrow-small': '/publishBall/icon-arrow-small.svg',
|
||||
'icon-map-search': '/publishBall/icon-map-search.svg',
|
||||
'icon-heartcircle': '/publishBall/icon-heartcircle.png',
|
||||
'icon-copy': '/publishBall/icon-arrow-right.svg',
|
||||
'icon-delete': '/publishBall/icon-delete.svg',
|
||||
'icon-circle-unselect': '/publishBall/icon-circle-unselect.svg',
|
||||
'icon-circle-select-ring': '/publishBall/icon-circle-select-ring.svg',
|
||||
'icon-circle-select-arrow': '/publishBall/icon-circle-select-arrow.svg',
|
||||
) !default;
|
||||
|
||||
// 图片获取函数
|
||||
|
||||
41
src/services/detailApi.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import httpService from './httpService'
|
||||
import type { ApiResponse } from './httpService'
|
||||
|
||||
// 用户接口
|
||||
export interface GameDetail {
|
||||
id: number,
|
||||
title: string,
|
||||
venue_id: number,
|
||||
creator_id: number,
|
||||
game_date: string,
|
||||
start_time: string,
|
||||
end_time: string,
|
||||
max_participants: number,
|
||||
current_participants: number,
|
||||
ntrp_level: string,
|
||||
play_style: string,
|
||||
description: string,
|
||||
status: string,
|
||||
created_at: string,
|
||||
updated_at: string,
|
||||
}
|
||||
|
||||
// 响应接口
|
||||
export interface Response {
|
||||
code: string
|
||||
message: string
|
||||
data: GameDetail
|
||||
}
|
||||
|
||||
// 发布球局类
|
||||
class GameDetailService {
|
||||
// 用户登录
|
||||
async getDetail(id: number): Promise<ApiResponse<Response>> {
|
||||
return httpService.post('/games/detail', { id }, {
|
||||
showLoading: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 导出认证服务实例
|
||||
export default new GameDetailService()
|
||||
@@ -15,6 +15,7 @@ export interface RequestConfig {
|
||||
needAuth?: boolean // 是否需要token认证
|
||||
showLoading?: boolean // 是否显示加载提示
|
||||
loadingText?: string // 加载提示文本
|
||||
showToast?: boolean // 是否显示toast
|
||||
}
|
||||
|
||||
// 响应数据接口
|
||||
@@ -58,7 +59,7 @@ class HttpService {
|
||||
// 构建完整URL
|
||||
private buildUrl(url: string, params?: Record<string, any>): string {
|
||||
const fullUrl = url.startsWith('http') ? url : `${this.baseURL}${url}`
|
||||
|
||||
|
||||
if (params) {
|
||||
const searchParams = new URLSearchParams()
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
@@ -69,7 +70,7 @@ class HttpService {
|
||||
const queryString = searchParams.toString()
|
||||
return queryString ? `${fullUrl}?${queryString}` : fullUrl
|
||||
}
|
||||
|
||||
|
||||
return fullUrl
|
||||
}
|
||||
|
||||
@@ -95,7 +96,7 @@ class HttpService {
|
||||
|
||||
const logMethod = console[level] || console.log
|
||||
const timestamp = new Date().toLocaleTimeString()
|
||||
|
||||
|
||||
if (data) {
|
||||
logMethod(`[${timestamp}] HTTP ${level.toUpperCase()}: ${message}`, data)
|
||||
} else {
|
||||
@@ -165,9 +166,9 @@ class HttpService {
|
||||
// 处理业务错误
|
||||
private handleBusinessError(data: any): void {
|
||||
const message = data.message || '操作失败'
|
||||
|
||||
|
||||
this.log('error', `业务错误: ${message}`, data)
|
||||
|
||||
|
||||
Taro.showToast({
|
||||
title: message,
|
||||
icon: 'none',
|
||||
@@ -187,7 +188,7 @@ class HttpService {
|
||||
} = config
|
||||
|
||||
const fullUrl = this.buildUrl(url, method === 'GET' ? params : undefined)
|
||||
|
||||
|
||||
this.log('info', `发起请求: ${method} ${fullUrl}`, {
|
||||
data: method !== 'GET' ? data : undefined,
|
||||
params: method === 'GET' ? params : undefined
|
||||
@@ -223,18 +224,18 @@ class HttpService {
|
||||
return this.handleResponse<T>(response)
|
||||
} catch (error) {
|
||||
this.log('error', '请求失败', error)
|
||||
|
||||
|
||||
// 在模拟模式下返回模拟数据
|
||||
if (envConfig.enableMock && isDevelopment()) {
|
||||
this.log('info', '使用模拟数据')
|
||||
return this.getMockResponse<T>(url, method)
|
||||
}
|
||||
|
||||
|
||||
Taro.showToast({
|
||||
title: '网络连接失败',
|
||||
icon: 'none'
|
||||
})
|
||||
|
||||
|
||||
throw error
|
||||
} finally {
|
||||
// 隐藏加载提示
|
||||
@@ -247,7 +248,7 @@ class HttpService {
|
||||
// 获取模拟数据
|
||||
private getMockResponse<T>(url: string, method: string): ApiResponse<T> {
|
||||
this.log('info', `返回模拟数据: ${method} ${url}`)
|
||||
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
success: true,
|
||||
@@ -323,4 +324,4 @@ class HttpService {
|
||||
}
|
||||
|
||||
// 导出HTTP服务实例
|
||||
export default new HttpService()
|
||||
export default new HttpService()
|
||||
@@ -16,6 +16,12 @@ export interface PublishBallData {
|
||||
description: string,
|
||||
}
|
||||
|
||||
export interface createGameData extends PublishBallData {
|
||||
status: string,
|
||||
created_at: string,
|
||||
updated_at: string,
|
||||
}
|
||||
|
||||
// 响应接口
|
||||
export interface Response {
|
||||
code: string
|
||||
@@ -23,16 +29,74 @@ export interface Response {
|
||||
data: any
|
||||
}
|
||||
|
||||
// export type SourceType = 'history' | 'preset'
|
||||
|
||||
export interface getPicturesReq {
|
||||
pageOption: {
|
||||
page: number,
|
||||
pageSize: number,
|
||||
},
|
||||
seachOption: {
|
||||
tag: string,
|
||||
resource_type: string,
|
||||
dateRange: string[],
|
||||
},
|
||||
}
|
||||
|
||||
export interface getPicturesRes {
|
||||
code: number,
|
||||
message: string,
|
||||
data: {
|
||||
rows: [
|
||||
{
|
||||
user_id: string,
|
||||
resource_type: string,
|
||||
file_name: string,
|
||||
original_name: string,
|
||||
file_path: string,
|
||||
file_url: string,
|
||||
file_size: number,
|
||||
mime_type: string,
|
||||
width: number,
|
||||
height: number,
|
||||
duration: number,
|
||||
thumbnail_url: string,
|
||||
is_public: string,
|
||||
tags: string[],
|
||||
description: string,
|
||||
view_count: number,
|
||||
download_count: number,
|
||||
status: string,
|
||||
last_modify_time: string,
|
||||
}
|
||||
],
|
||||
count: number,
|
||||
page: number,
|
||||
pageSize: number,
|
||||
totalPages: number,
|
||||
}
|
||||
}
|
||||
|
||||
function delay(ms: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
// 发布球局类
|
||||
class PublishService {
|
||||
// 用户登录
|
||||
async createPersonal(data: PublishBallData): Promise<ApiResponse<Response>> {
|
||||
async createPersonal(data: PublishBallData): Promise<ApiResponse<createGameData>> {
|
||||
return httpService.post('/games/create', data, {
|
||||
showLoading: true,
|
||||
loadingText: '发布中...'
|
||||
})
|
||||
}
|
||||
|
||||
async getPictures(req: getPicturesReq): Promise<ApiResponse<getPicturesRes>> {
|
||||
return httpService.post('/gallery/sys_img_list', req, {
|
||||
showLoading: true,
|
||||
showToast: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 导出认证服务实例
|
||||
export default new PublishService()
|
||||
export default new PublishService()
|
||||
75
src/services/uploadFiles.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import httpService from './httpService'
|
||||
import type { ApiResponse } from './httpService'
|
||||
import Taro from '@tarojs/taro'
|
||||
import envConfig from '@/config/env'
|
||||
|
||||
// 用户接口
|
||||
export interface UploadFilesData {
|
||||
id: string,
|
||||
filePath: string,
|
||||
description?: string,
|
||||
tags?: string,
|
||||
is_public?: 0 | 1,
|
||||
}
|
||||
|
||||
// {"code":0,"message":"请求成功!","data":{"tags":["test"],"create_time":"2025-08-24 22:51:03","last_modify_time":"2025-08-24 22:51:03","duration":"0","thumbnail_url":"","view_count":"0","download_count":"0","id":16,"user_id":1,"resource_type":"image","file_name":"front/ball/images/63f56978-ffe9-4397-b897-8aca6f4fdcd8.png","original_name":"QyoUvEsLG6ci57c7e25cca0845dafed3ee1fde07876d.png","file_path":"http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/63f56978-ffe9-4397-b897-8aca6f4fdcd8.png","file_url":"http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/63f56978-ffe9-4397-b897-8aca6f4fdcd8.png","file_size":17756,"mime_type":"image/png","description":"test","is_public":"1","status":"active","width":0,"height":0,"uploadInfo":{"success":true,"name":"front/ball/images/63f56978-ffe9-4397-b897-8aca6f4fdcd8.png","path":"http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/63f56978-ffe9-4397-b897-8aca6f4fdcd8.png","ossPath":"http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/63f56978-ffe9-4397-b897-8aca6f4fdcd8.png","fileType":"image/png","fileSize":17756,"originalName":"QyoUvEsLG6ci57c7e25cca0845dafed3ee1fde07876d.png","suffix":"png","storagePath":"front/ball/images/63f56978-ffe9-4397-b897-8aca6f4fdcd8.png"}}}
|
||||
|
||||
export interface uploadFileResponse {
|
||||
code: number,
|
||||
message: string,
|
||||
data: uploadFileResponseData,
|
||||
}
|
||||
|
||||
export interface uploadFileResponseData {
|
||||
id: number,
|
||||
user_id: number,
|
||||
file_name: string,
|
||||
original_name: string,
|
||||
file_path: string,
|
||||
file_url: string,
|
||||
file_size: number,
|
||||
resource_type: string,
|
||||
mime_type: string,
|
||||
description: string,
|
||||
tags: string[],
|
||||
is_public: string,
|
||||
view_count: number,
|
||||
download_count: number,
|
||||
created_at: string,
|
||||
updated_at: string,
|
||||
}
|
||||
|
||||
function delay(ms: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
// 发布球局类
|
||||
class UploadApi {
|
||||
async upload(req: UploadFilesData): Promise<{ id: string, data: uploadFileResponseData }> {
|
||||
// return httpService.post('/files/upload', req, {
|
||||
// showLoading: true,
|
||||
// })
|
||||
const { id, ...rest } = req
|
||||
return Taro.uploadFile({
|
||||
url: `${envConfig.apiBaseURL}/api/gallery/upload`,
|
||||
filePath: rest.filePath,
|
||||
name: 'file',
|
||||
formData: {
|
||||
description: rest.description,
|
||||
tags: rest.tags,
|
||||
is_public: rest.is_public,
|
||||
}
|
||||
}).then(res => {
|
||||
return {
|
||||
id,
|
||||
data: JSON.parse(res.data).data,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async batchUpload(req: UploadFilesData[]): Promise<{ id: string, data: uploadFileResponseData }[]> {
|
||||
return Promise.all(req.map(item => this.upload(item)))
|
||||
}
|
||||
}
|
||||
|
||||
// 导出认证服务实例
|
||||
export default new UploadApi()
|
||||
|
Before Width: | Height: | Size: 205 B After Width: | Height: | Size: 205 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
src/static/emptyStatus/comment-empty.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/static/emptyStatus/comment-failed.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
src/static/emptyStatus/publish-empty.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
src/static/emptyStatus/publish-failed.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
3
src/static/publishBall/icon-circle-select-arrow.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="7" viewBox="0 0 8 7" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.756715 3.24011C1.0258 2.96252 1.46896 2.95563 1.74654 3.22471L3.19002 4.62398L6.228 0.32133C6.45099 0.00551998 6.88777 -0.0697295 7.20358 0.153256C7.51939 0.376242 7.59464 0.813022 7.37165 1.12883L3.8619 6.09963C3.74393 6.26671 3.55878 6.37384 3.35514 6.39285C3.15149 6.41186 2.94972 6.34085 2.80286 6.19849L0.772108 4.22994C0.494524 3.96085 0.487632 3.51769 0.756715 3.24011Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 545 B |
3
src/static/publishBall/icon-circle-select-ring.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none">
|
||||
<circle cx="7" cy="7" r="7" fill="#161823"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 149 B |
3
src/static/publishBall/icon-circle-unselect.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none">
|
||||
<circle cx="7" cy="7" r="6.475" stroke="#161823" stroke-opacity="0.34" stroke-width="1.05"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 197 B |