diff --git a/src/components/article/block.tsx b/src/components/article/block.tsx index 032d316..6eedf46 100644 --- a/src/components/article/block.tsx +++ b/src/components/article/block.tsx @@ -9,6 +9,7 @@ import {Button, Popconfirm} from "antd"; type Props = { children?: React.ReactNode; + index?:number; className?: string; blocks: BlockContent[]; editable?: boolean; @@ -17,7 +18,7 @@ type Props = { onAdd?: () => void; } -export default function ArticleBlock({className, blocks, editable, onRemove, onAdd, onChange}: Props) { +export default function ArticleBlock({className, blocks, editable, onRemove, onAdd, onChange,index}: Props) { const handleBlockRemove = (index: number) => { // 删除当前项 onChange?.(blocks.filter((_, idx) => index !== idx)) @@ -47,7 +48,7 @@ export default function ArticleBlock({className, blocks, editable, onRemove, onA
{ it.type === 'text' - ? handleBlockChange(idx, block)} data={it} + ? handleBlockChange(idx, block)} data={it} editable={editable}/> : } diff --git a/src/components/article/edit-modal.tsx b/src/components/article/edit-modal.tsx index cf0decb..be2a3ba 100644 --- a/src/components/article/edit-modal.tsx +++ b/src/components/article/edit-modal.tsx @@ -2,42 +2,56 @@ import {Input, Modal} from "antd"; import ArticleGroup from "@/components/article/group.tsx"; import {useEffect, useState} from "react"; import {useSetState} from "ahooks"; +import {getArticleDetail} from "@/service/api/article.ts"; type Props = { - title?: string; - groups?: ArticleContentGroup[]; - onSave?: () => Promise; + id?: number; + onClose?: () => void; } export default function ArticleEditModal(props: Props) { - const [groups, setGroups] = useState([]); - const [title, setTitle] = useState(props.title) + const [groups, setGroups] = useState([]); + const [title, setTitle] = useState('') + const [state, setState] = useSetState({ loading: false, open: false }) const handleSave = () => { - setState({loading: true}) - props.onSave?.().finally(() => { - setState({loading: false,open: false}) - }) + props.onClose?.() + // if (props.onSave) { + // setState({loading: true}) + // props.onSave?.().then(() => { + // setState({loading: false, open: false}) + // }) + // } else { + // console.log(groups) + // } } useEffect(() => { - setState({open: typeof(props.title) != "undefined"}) - setGroups(props.groups || []) - setTitle(props.title||'') - }, [props.title,props.groups]) + if(props.id){ + if(props.id > 0){ + getArticleDetail(props.id).then(res => { + setGroups(res.content_group) + setTitle(res.title) + }) + }else{ + setGroups([]) + setTitle('') + } + } + }, [props.id]) return (= 0} maskClosable={false} keyboard={false} width={800} - onCancel={()=>setState({open: false})} + onCancel={props.onClose} okButtonProps={{loading: state.loading}} - coOk={handleSave} + onOk={handleSave} >
diff --git a/src/components/article/group.tsx b/src/components/article/group.tsx index bef3b63..04f8e2f 100644 --- a/src/components/article/group.tsx +++ b/src/components/article/group.tsx @@ -5,9 +5,9 @@ import ArticleBlock from "@/components/article/block.tsx"; import styles from './article.module.scss' type Props = { - groups: ArticleContentGroup[]; + groups: BlockContent[][]; editable?: boolean; - onChange?: (groups: ArticleContentGroup[]) => void; + onChange?: (groups: BlockContent[][]) => void; } export default function ArticleGroup({groups, editable, onChange}: Props) { /** @@ -15,7 +15,7 @@ export default function ArticleGroup({groups, editable, onChange}: Props) { * @param insertIndex 插入的位置,-1表示插入到末尾 */ const handleAddGroup = ( insertIndex: number = -1) => { - const newGroup: ArticleContentGroup = {blocks: []} + const newGroup: BlockContent[] = [] const _groups = [...groups] if (insertIndex == -1 || insertIndex >= groups.length) { // -1或者越界表示新增 _groups.push(newGroup) @@ -27,11 +27,12 @@ export default function ArticleGroup({groups, editable, onChange}: Props) { return
{groups.map((g, index) => ( { - groups[index].blocks = blocks + groups[index] = blocks onChange?.([...groups]) }} + index={index} onAdd={() => { handleAddGroup?.(index + 1) }} @@ -45,6 +46,6 @@ export default function ArticleGroup({groups, editable, onChange}: Props) { /> ))} {groups.length == 0 && editable && - onChange?.([{blocks}])} blocks={[]}/>} + onChange?.([blocks])} blocks={[]}/>}
} \ No newline at end of file diff --git a/src/components/article/item.tsx b/src/components/article/item.tsx index c28f701..2b62dd7 100644 --- a/src/components/article/item.tsx +++ b/src/components/article/item.tsx @@ -1,24 +1,28 @@ -import React from "react"; -import styles from './article.module.scss' +import React, {useMemo, useRef, useState} from "react"; import {Button, Input, Upload} from "antd"; +import {TextAreaRef} from "antd/es/input/TextArea"; + +import styles from './article.module.scss' type Props = { children?: React.ReactNode; className?: string; data: BlockContent; editable?: boolean; + groupIndex?: number; + blockIndex?: number; onChange?: (data: BlockContent) => void; } -export function BlockImage({data,editable}: Props) { +export function BlockImage({data, editable}: Props) { return
{editable ?
-
- { data.content ? <> +
+ {data.content ? <>
- 编辑 + 更换图片
:
@@ -29,14 +33,65 @@ export function BlockImage({data,editable}: Props) {
} -export function BlockText({data,editable,onChange}: Props) { +export function BlockText({data, editable, onChange, groupIndex,blockIndex}: Props) { + const inputRef = useRef(null); + // 内容分割 + const contentSentence = useMemo(() => { + const textContent = data.content + if (!/[.|。]/.test(textContent)) { + return [textContent]; + } + const firstSentence = textContent.split(/[.|。]/)[0]! + // 获取第一个句子 + return [textContent.substring(0, firstSentence.length + 1), textContent.substring(firstSentence.length + 1)] + }, [data.content]) + + const [editorMode, setEditMode] = useState({ + preview: true + }) + const handleTextBlur = () => { + setEditMode({preview: true}) + } return
- {editable ?
- {/**/} + { + onChange?.({type: 'text', content: e.target.value}) + }} + placeholder={'请输入文本'} onBlur={handleTextBlur} value={data.content} autoSize={{minRows: 3}} + variant={"borderless"}/> + {groupIndex == 0 && blockIndex == 0 && +
{ + inputRef.current!.focus({cursor: 'end'}); + setEditMode({preview: false}) + }} style={editorMode.preview && data.content?.length > 0 ? { + padding: '4px 11px' + } : { + opacity: 0, + pointerEvents: 'none' + }}> + {contentSentence.map((sentence, index) => { + return {sentence}{index == 0 ? '(本句由数字人播报)' : ''} + })} +
} + {/*{firstSentence}*/} + {/**/} +
:

{data.content}

}
} \ No newline at end of file diff --git a/src/components/form/sms-code.tsx b/src/components/form/sms-code.tsx index 61da70f..a97230f 100644 --- a/src/components/form/sms-code.tsx +++ b/src/components/form/sms-code.tsx @@ -5,6 +5,7 @@ import {useState} from "react"; import InputContainer from "@/components/form/input-container.tsx"; import {clsx} from "clsx"; +import {sendSmsCode} from "@/service/api/user.ts"; type Props = { onChange?: (code: string) => void; @@ -23,10 +24,11 @@ export function useSmsCode() { const sendCode = (phone?:string,interval = 60) => { if (countdown > 0 || sending || !phone) return; setSending(true) - setTimeout(() => { + sendSmsCode(phone).then(()=>{ setTargetDate(Date.now() + interval * 1000) + }).finally(()=>{ setSending(false) - }, 500) + }) } return { sendCode, diff --git a/src/components/icons/user-avatar.tsx b/src/components/icons/user-avatar.tsx index 8664d09..8737308 100644 --- a/src/components/icons/user-avatar.tsx +++ b/src/components/icons/user-avatar.tsx @@ -1,10 +1,19 @@ -import Avatar from "@/assets/images/avatar.png"; +// import Avatar from "@/assets/images/avatar.png"; import React from "react"; -export const UserAvatar = ( {className,style}: { style?: React.CSSProperties;className?: string }) => { +export const UserAvatar = ({className, style}: { style?: React.CSSProperties; className?: string }) => { return ( - + {/* */} + + + + ) } \ No newline at end of file diff --git a/src/components/video/video-list-item.tsx b/src/components/video/video-list-item.tsx index 0cc1dc4..02f4573 100644 --- a/src/components/video/video-list-item.tsx +++ b/src/components/video/video-list-item.tsx @@ -9,6 +9,8 @@ import {Popconfirm} from "antd"; type Props = { video: VideoInfo, + editable?: boolean; + sortable?: boolean; index?: number; checked?: boolean; active?: boolean; @@ -16,17 +18,22 @@ type Props = { onPlay?: () => void; onEdit?: () => void; onRemove?: () => void; - id:number; + id: number; } -export const VideoListItem = ({index,id, video, onPlay, onRemove, checked, onCheckedChange,onEdit,active}: Props) => { +export const VideoListItem = ( + { + index, id, video, onPlay, onRemove, checked, + onCheckedChange, onEdit, active, editable, + sortable + }: Props) => { const { attributes, listeners, setNodeRef, transform } = useSortable({resizeObserverConfig: {}, id}) - const [state, setState] = useSetState<{checked?:boolean}>({}) + const [state, setState] = useSetState<{ checked?: boolean }>({}) useEffect(() => { setState({checked}) }, [checked]) @@ -38,34 +45,41 @@ export const VideoListItem = ({index,id, video, onPlay, onRemove, checked, onChe
{id}
} -
+
{video.id} - {video.title}
{video.title}/
-
- - {onPlay && } - {onEdit && } - - {onRemove && 请确认删除此视频?
} - onConfirm={onRemove} - okText="删除" - cancelText="取消" - >} -
+ {editable && +
+ {!active ? : } + {onPlay && + } + {onEdit && + } + + {onRemove && 请确认删除此视频?
} + onConfirm={onRemove} + okText="删除" + cancelText="取消" + > + + } +
+ }
} \ No newline at end of file diff --git a/src/contexts/auth/index.tsx b/src/contexts/auth/index.tsx index 7349902..6c78c56 100644 --- a/src/contexts/auth/index.tsx +++ b/src/contexts/auth/index.tsx @@ -32,7 +32,7 @@ const initialState: AuthProps = { }; // 状态 reducer -const authReducer = (prevState: AuthProps, {payload}:AuthAction ) => { +const authReducer = (prevState: AuthProps, {payload}: AuthAction) => { return { ...prevState, ...payload, @@ -45,50 +45,43 @@ export const AuthProvider = ({children}: { children: React.ReactNode }) => { // MOCK INIT DATA const init = async () => { const token = getAuthToken(); - if (!token) { - dispatch({ - payload: { - isInitialized: true, - } - }) - return 'initialized' - } - getUserInfo().then(user => { - dispatch({ - action: 'init', - payload: { - isInitialized: true, - isLoggedIn: !!user, - user: { - ...user, - role: getCurrentRole() + if (token) { + const result = localStorage.getItem(AppConfig.AUTHED_PERSON_DATA_KEY) + if (result) { + const user = JSON.parse(result) as UserProfile + dispatch({ + payload: { + isInitialized: true, + isLoggedIn: true, + user, + token } - } - }) - }).finally(() => { - dispatch({ - payload: { - isInitialized: true, - } - }) + }) + return 'initialized' + } + } + dispatch({ + payload: { + isInitialized: true, + user: null, + token: null + } }) return 'initialized' } // 登录 const login = async (code: string, state: string) => { - const user = await auth(code, state) + const ret = await auth(code, state) // 保存token - setAuthToken(user.token, user.expiration_time ? (new Date(user.expiration_time)).getTime() : -1); + setAuthToken(ret.token, ret.user_info, -1); // dispatch({ action: 'login', payload: { isLoggedIn: true, - user: { - ...user, - role: getCurrentRole() - } + token: ret.token, + user: ret.user_info } }) } diff --git a/src/hooks/useArticleTags.ts b/src/hooks/useArticleTags.ts new file mode 100644 index 0000000..7a19020 --- /dev/null +++ b/src/hooks/useArticleTags.ts @@ -0,0 +1,42 @@ +import {useCallback, useEffect, useState} from "react"; +import {getAllCategory} from "@/service/api/article.ts"; + + +const ArticleTags: OptionItem[] = []; +export default function useArticleTags() { + const [tags, _setTags] = useState([]); + const setTags = useCallback(() => { + _setTags([ + { + label: '全部', + value: -1, + }, + ...ArticleTags + ]) + }, []) + useEffect(() => { + if (ArticleTags.length === 0) { + getAllCategory().then(res => { + ArticleTags.length = 0; + res.tags.forEach(t => { + const item = { + label: t.tag_name, + value: t.tag_id, + children: t.sons && t.sons.length > 0 ? t.sons.map(s => ({ + label: s.tag_name, + value: s.tag_id + })) : [] + }; + ArticleTags.push(item) + }) + setTags() + }) + } + return () => { + // 清除 + setTags([]) + ArticleTags.length = 0; + } + }, []) + return tags +} \ No newline at end of file diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts index b3a7313..ba9cc0b 100644 --- a/src/hooks/useAuth.ts +++ b/src/hooks/useAuth.ts @@ -13,14 +13,20 @@ const useAuth = () => { return context; }; -export const setAuthToken = (token: string | null, expiry_time = -1) => { + +const clearAuth = () => { + localStorage.removeItem(AppConfig.AUTH_TOKEN_KEY); + localStorage.removeItem(AppConfig.AUTHED_PERSON_DATA_KEY); +} +export const setAuthToken = (token: string | null,profileData:UserProfile, expiry_time = -1) => { if (!token) { - localStorage.removeItem(AppConfig.AUTH_TOKEN_KEY); + clearAuth(); return; } localStorage.setItem(AppConfig.AUTH_TOKEN_KEY, JSON.stringify({ token, expiry_time })); + localStorage.setItem(AppConfig.AUTHED_PERSON_DATA_KEY, JSON.stringify(profileData)); } export const getAuthToken = () => { @@ -29,7 +35,7 @@ export const getAuthToken = () => { try { const {token, expiry_time} = JSON.parse(data) as { token: string, expiry_time: number }; if (expiry_time != -1 && expiry_time < Date.now()) { - localStorage.removeItem(AppConfig.AUTH_TOKEN_KEY); + clearAuth(); return; } return token; diff --git a/src/pages/create/index.tsx b/src/pages/create/index.tsx index f70f32d..b4771ec 100644 --- a/src/pages/create/index.tsx +++ b/src/pages/create/index.tsx @@ -108,6 +108,7 @@ export default function CreateIndex() { onEdit={() => { setEditNews({title:v.title, groups: [...ArticleGroupList]}) }} + editable />))} diff --git a/src/pages/library/components/search-form.tsx b/src/pages/library/components/search-form.tsx index adb9e3d..2fbb21f 100644 --- a/src/pages/library/components/search-form.tsx +++ b/src/pages/library/components/search-form.tsx @@ -1,5 +1,7 @@ -import {Button, DatePicker, Form, Input, Space} from "antd"; +import {Button, Form, Input, Select, Space} from "antd"; import {useSetState} from "ahooks"; +import {PlayCircleOutlined} from "@ant-design/icons"; +import {ListTimes} from "@/pages/news/components/news-source.ts"; type SearchParams = { keywords?: string; @@ -15,8 +17,9 @@ export default function SearchForm({onSearch, onBtnStartClick}: Props) { timeRange: string; keywords: string; searching: boolean; + time: string; }>({ - keywords: "", searching: false, timeRange: "" + keywords: "", searching: false, timeRange: "", time: '-1' }) const onFinish = (values: any) => { setState({searching: true}) @@ -29,26 +32,39 @@ export default function SearchForm({onSearch, onBtnStartClick}: Props) { } return (
-
+
- + - - - - - - - - + + ( -
- - {option.label} -
- )} - labelRender={(props) => { - if (props.value == 'all') return 全部 - - return {props.label} + className="w-[250px]" + onChange={e=>{ + console.log('e.target.value',e) }} + displayRender={label => label.join('-')} + expandTrigger="hover" + multiple + maxTagCount="responsive" /> + {/* (*/} + {/*
*/} + {/* */} + {/* {option.label}*/} + {/*
*/} + {/* )}*/} + {/* labelRender={(props) => {*/} + {/* if (props.value == 'all') return 全部*/} + {/* return {props.label}*/} + {/* }}*/} + {/*/>*/}
- +
- + rowSelection={{type: 'checkbox', ...rowSelection}} columns={columns} - dataSource={data as any} + dataSource={data?.list||[]} rowKey={'id'} + bordered pagination={{ position: ['bottomLeft'], simple: true, - defaultCurrent: 1, - total: 5000004, - pageSize: 20, + defaultCurrent: params.page, + total: data?.pagination.total || 0, + pageSize: params.limit, showSizeChanger: false, rootClassName: 'simple-pagination', onChange: (page) => setParams({page}) }} />
- + setEditId(-1)} />
) } \ No newline at end of file diff --git a/src/pages/user/components/form-login.tsx b/src/pages/user/components/form-login.tsx index 9498462..a94f52f 100644 --- a/src/pages/user/components/form-login.tsx +++ b/src/pages/user/components/form-login.tsx @@ -33,7 +33,7 @@ export default function FormLogin() { navigate(params.get('from') || '/') }).catch(e => { setError(e.message) - }).finally(()=>setLoading(false)); + }).finally(() => setLoading(false)); }; return (
@@ -54,7 +54,7 @@ export default function FormLogin() {
- +
@@ -83,8 +83,9 @@ export default function FormLogin() { - diff --git a/src/pages/user/index.tsx b/src/pages/user/index.tsx index 11ddc55..3722d62 100644 --- a/src/pages/user/index.tsx +++ b/src/pages/user/index.tsx @@ -1,7 +1,18 @@ import styles from './style.module.scss' import FormLogin from "./components/form-login.tsx"; +import useAuth from "@/hooks/useAuth.ts"; +import {useNavigate, useSearchParams} from "react-router-dom"; +import {useEffect} from "react"; export default function UserIndex(){ + const {user} = useAuth(); + const navigate = useNavigate() ; + const [param] = useSearchParams() + useEffect(() => { + if (user) { + navigate(param.get('from') || '/') + } + }, [user]) return (
diff --git a/src/routes/layout/dashboard-layout.tsx b/src/routes/layout/dashboard-layout.tsx index 40a1f53..7f50d6a 100644 --- a/src/routes/layout/dashboard-layout.tsx +++ b/src/routes/layout/dashboard-layout.tsx @@ -9,21 +9,22 @@ import {UserAvatar} from "@/components/icons/user-avatar.tsx"; import {DashboardNavigation} from "@/routes/layout/dashboard-navigation.tsx"; import useAuth from "@/hooks/useAuth.ts"; +import {hidePhone} from "@/util/strings.ts"; type LayoutProps = { children: React.ReactNode } const NavigationUserContainer = () => { - const {logout} = useAuth() + const {logout,user} = useAuth() const navigate = useNavigate() const items: MenuProps['items'] = [ { - key: '1', + key: 'profile', label: '个人中心', }, { - key: '2', + key: 'logout', label:
{ logout().then(()=>navigate('/user')) }}>退出
, @@ -31,9 +32,9 @@ const NavigationUserContainer = () => { ]; return (
-
+
- 180xxxx7788 + {hidePhone(user?.nickname)}
) diff --git a/src/service/api/article.ts b/src/service/api/article.ts new file mode 100644 index 0000000..85cd21a --- /dev/null +++ b/src/service/api/article.ts @@ -0,0 +1,31 @@ +import {post} from "@/service/request.ts"; + +export function getAllCategory() { + return post<{ tags: ArticleCategory[] }>({url: '/spider/tags'}) +} + +export function getArticleList(data: ApiArticleSearchParams & ApiRequestPageParams) { + return post>({url: '/article/search', data}) +} + +/** + * 删除 【本期不做】 + * @param id + */ +export function deleteArticle(id: Id) { + throw new Error('Not implement') + return post<{ article: any }>({url: '/article/delete/' + id}) +} +export function getArticleDetail(id: Id) { + return post({url: '/article/detail/' + id}) +} +export function saveArticle(title:string,content_group: BlockContent[][],id: number) { + return post<{ content: string }>({ + url: '/spider/article', + data:{ + title, + content_group, + id + } + }) +} \ No newline at end of file diff --git a/src/service/api/common.ts b/src/service/api/common.ts new file mode 100644 index 0000000..78db1f5 --- /dev/null +++ b/src/service/api/common.ts @@ -0,0 +1,13 @@ +import {post} from "@/service/request.ts"; + +export function getOssPolicy(scene = 'workbench') { + return post({ + data: {scene}, + baseURL: '/api/v1/common/get_oss_policy', + url: `/api/v1/common/get_oss_policy` + }) +} + +export function uploadToOss(file: File, policy: TOSSPolicy) { + +} \ No newline at end of file diff --git a/src/types/user.d.ts b/src/service/api/news.ts similarity index 100% rename from src/types/user.d.ts rename to src/service/api/news.ts diff --git a/src/service/api/user.ts b/src/service/api/user.ts index 2eb3fcf..1b51788 100644 --- a/src/service/api/user.ts +++ b/src/service/api/user.ts @@ -1,27 +1,22 @@ -import {sleep} from "@/util/basic.ts"; - -const mockUser: UserProfile = { - id: 1, - token: '123', - email: 'admin@qq.com', - username: 'admin', - role: 'normal', - expiration_time: '2025-12-31 23:23:23', -} - -export async function getUserInfo() { - await sleep(500); - return mockUser; - // return get('/userinfo') -} +import {post} from "@/service/request.ts"; /** - * 使用 sso 返回的code、state换取登录凭证或用户信息 + * 登录并返回 token * @param code - * @param state + * @param phone */ -export async function auth(_username: string, _password: string) { - await sleep(1500); - return mockUser; - //return post('/auth', {code, state}) +export async function auth(phone: string, code: string) { + return post<{ + token: string; + user_info: UserProfile + }>({ + url: '/login', + data: {code, phone} + }) +} + +export function sendSmsCode(phone: string) { + return post({ + url:'/smscode', data:{phone} + }) } \ No newline at end of file diff --git a/src/service/request.ts b/src/service/request.ts index c5ce661..676248f 100644 --- a/src/service/request.ts +++ b/src/service/request.ts @@ -7,74 +7,86 @@ const JSON_FORMAT: string = 'application/json'; const REQUEST_TIMEOUT = 300000; // 超时时长5min const Axios = axios.create({ - baseURL: AppConfig.API_PREFIX || '/api', - timeout: REQUEST_TIMEOUT, - headers: {'Content-Type': JSON_FORMAT} + timeout: REQUEST_TIMEOUT, + headers: {'Content-Type': JSON_FORMAT} }) // 请求前拦截 Axios.interceptors.request.use(config => { - const token = getAuthToken(); - if (token) { - config.headers['Token'] = `${token}`; - } - if (config.data && config.data instanceof FormData) { - config.headers['Content-Type'] = 'multipart/form-data'; - } - return config + const token = getAuthToken(); + if (token) { + config.headers['Token'] = `${token}`; + } + if (config.data && config.data instanceof FormData) { + config.headers['Content-Type'] = 'multipart/form-data'; + } + return config }, err => { - return Promise.reject(err) + return Promise.reject(err) }) -export function request(url: string, method: RequestMethod, data: AllType = null, getOriginResult = false) { - return new Promise((resolve, reject) => { - Axios.request>({ - url, - method, - data, - }).then(res => { - if (res.status != 200) { - reject(new BizError("Service Internal Exception,Please Try Later!", res.status)) - return; - } - if (getOriginResult) { - resolve(res.data as unknown as T) - return; - } - // const - const {code, message, data,request_id} = res.data - if (code == 0) { - resolve(data as unknown as T) - } else { - reject(new BizError(message, code,request_id, data as unknown as AllType)) - } - }).catch(e => { - reject(new BizError(e.message, 500)) - }) - }) + +export function request(options: RequestOption) { + return new Promise((resolve, reject) => { + const {url, method, data, baseURL, getOriginResult} = options; + + Axios.request>({ + url, + method: method || 'get', + data, + baseURL: baseURL || AppConfig.API_PREFIX, + }).then(res => { + if (res.status != 200) { + reject(new BizError("Service Internal Exception,Please Try Later!", res.status)) + return; + } + if (getOriginResult) { + resolve(res.data as unknown as T) + return; + } + // const + const {code, message, data, request_id} = res.data + if (code == 0) { + resolve(data as unknown as T) + } else { + reject(new BizError(message, code, request_id, data as unknown as AllType)) + } + }).catch(e => { + reject(new BizError(e.message, 500)) + }) + }) } -export function post(url: string, data: AllType = {}, returnOrigin = false) { - return request(url, 'post', data, returnOrigin) +export function post(params: RequestOption) { + return request({ + ...params, + method: 'post' + }) } -export function get(url: string, data: AllType = null, returnOrigin = false) { - if (data) { - url += (url.indexOf('?') === -1 ? '?' : '&') + stringify(data) - } - return request(url, 'get', data, returnOrigin) +export function get(params: RequestOption) { + if (params.data) { + params.url += (params.url.indexOf('?') === -1 ? '?' : '&') + stringify(params.data) + } + return request({ + ...params, + method: 'get' + }) } -export function put(url: string, data: AllType = {}) { - return request(url, 'put', data) +export function put(params: RequestOption) { + return request({ + ...params, + method: 'put' + }) } export function getFileBlob(url: string) { - return new Promise((resolve, reject) => { - fetch(url).then(res => res.blob()).then(res => { - resolve(res) - }).catch(reject); - }); + return new Promise((resolve, reject) => { + fetch(url).then(res => res.blob()).then(res => { + resolve(res) + }).catch(reject); + }); } diff --git a/src/service/types.ts b/src/service/types.ts index 18126c7..143c6b7 100644 --- a/src/service/types.ts +++ b/src/service/types.ts @@ -12,4 +12,4 @@ export class BizError extends Error { this.code = code; this.data = data; } -} +} \ No newline at end of file diff --git a/src/types/api.d.ts b/src/types/api.d.ts index 1205460..8f19b69 100644 --- a/src/types/api.d.ts +++ b/src/types/api.d.ts @@ -1,18 +1,38 @@ -// 请求方式 -declare type RequestMethod = 'get' | 'post' | 'put' | 'delete' +declare interface ApiRequestPageParams { + pagination: { + page: number; + limit: number; + } +} -// 接口返回数据类型 -declare interface APIResponse { - /** - * 错误码,0:成功,其他失败 - */ - code: number; - data?: T; - /** - * 非0情况下,提示信息 - */ - message: string; - request_id: string; +declare interface ApiArticleSearchParams { + // 1级标签id + tag_level_1_id?: number; + // 2级标签id 没有则为0 + tag_level_2_id?: number; + // 标题 + title?: string; +} + +declare interface DataList { + pagination: { + page: number; + limit: number; + total: number; + } + + list: T[]; +} + +interface BaseArticleCategory { + //标签id + tag_id: number; + //标签名称 + tag_name: string; +} + +interface ArticleCategory extends BaseArticleCategory { + sons: BaseArticleCategory[]; } declare interface VideoInfo { @@ -26,5 +46,32 @@ declare interface VideoInfo { description: string; tags: string[]; create_time: number; - checked?:boolean + checked?: boolean } + +interface BasicArticleInfo { + id: number; + title: string; + summary: string; + publish_time: string; + media_name: string; + media_id: number; + fanwen_column_id: number; +} + +/** + * 文章 + */ +declare interface ListArticleItem extends BasicArticleInfo { +} + +/** + * 爬虫新闻 + */ +declare interface ListCrawlerNewsItem extends BasicArticleInfo { + cover: string; + // 新闻来源 + data_source_name: string; + // 内部文章关联id + internal_article_id: number; +} \ No newline at end of file diff --git a/src/types/auth.d.ts b/src/types/auth.d.ts index 4f77929..ae70d82 100644 --- a/src/types/auth.d.ts +++ b/src/types/auth.d.ts @@ -1,25 +1,29 @@ declare type UserRole = string -declare type UserProfile = { - id: string | number; - token: string; - email: string; - username: string; - role: UserRole; - expiration_time?:number | string + +declare interface UserProfile { + id: number; + phone: string; + nickname: string; + realname: string; + status: number; + ctime: number; + utime: number; + dtime: number; } + declare interface AuthProps { - isLoggedIn: boolean; - isInitialized?: boolean; - user?: UserProfile | null; - token?: string | null; + isLoggedIn: boolean; + isInitialized?: boolean; + user?: UserProfile | null; + token?: string | null; } declare type AuthContextType = { - isLoggedIn: boolean; - isInitialized?: boolean; - user?: UserProfile | null | undefined; - logout: () => Promise; - login: (username:string,password:string) => Promise; - updateUser: (user:Partial) => Promise; + isLoggedIn: boolean; + isInitialized?: boolean; + user?: UserProfile | null | undefined; + logout: () => Promise; + login: (phone: string, code: string) => Promise; + updateUser: (user: Partial) => Promise; }; \ No newline at end of file diff --git a/src/types/core.d.ts b/src/types/core.d.ts index 127685d..c1aabe8 100644 --- a/src/types/core.d.ts +++ b/src/types/core.d.ts @@ -1,3 +1,5 @@ +declare type Id = number | string; + declare interface RecordList { list: T[]; pagination: { @@ -10,22 +12,25 @@ declare interface RecordList { }; } declare interface OptionItem { - label: string;value: string; + label: string; + value: string|number; children?: OptionItem[]; } + declare interface BlockContent { type: 'text' | 'image'; content: string; } + declare interface ArticleContentGroup { groupId?: number; blocks: BlockContent[]; } -declare interface ArticleInfo { +declare interface ArticleDetail { id: number; title: string; - content: ArticleContentGroup[] + content_group: BlockContent[][] } declare interface NewsInfo { @@ -35,4 +40,22 @@ declare interface NewsInfo { content: string; source: string; time: string|number; +} +declare interface TOSSPolicy { + //Oss access id + access_id: string; + //上传 host + host: string; + //Oss 上传 policy + policy: string; + //Oss 上传的鉴权签名 + signature: string; + //该签名和policy有效期截至时间 + expire: int; + //上传的 路径,在最后的 / 后面接上文件名称 文件名称 处理成 K8efE3imYn.docx,文件名长度16 + dir: string; + //允许上传的文件最大大小,单位字节 + max_size: int; + //允许上传的文件最大大小,单位字节 + allow: string; } \ No newline at end of file diff --git a/src/types/request.d.ts b/src/types/request.d.ts new file mode 100644 index 0000000..1e165b4 --- /dev/null +++ b/src/types/request.d.ts @@ -0,0 +1,24 @@ +// 请求方式 +declare type RequestMethod = 'get' | 'post' | 'put' | 'delete' +declare type RequestOption = { + url: string; + method?: RequestMethod; + data?: AllType | null; + getOriginResult?: boolean; + baseURL?: string; +} + +// 接口返回数据类型 +declare interface APIResponse { + /** + * 错误码,0:成功,其他失败 + */ + code: number; + /** + * 非0情况下,提示信息 + */ + msg: string; + cost: string; + trace_id: string; + data?: T; +} \ No newline at end of file diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 6d843ab..05fd961 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -8,6 +8,8 @@ declare const APP_SITE_URL: string; declare const AppConfig: { // 登录凭证 token key AUTH_TOKEN_KEY: string; + // 登录用户信息 key + AUTHED_PERSON_DATA_KEY: string; API_PREFIX: string; }; declare const AppMode: 'test' | 'production' | 'development'; diff --git a/vite.config.ts b/vite.config.ts index 3fa64d3..0cfb3e1 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -10,8 +10,9 @@ export default defineConfig(({mode}) => { define: { AppConfig: JSON.stringify({ SITE_URL: process.env.APP_SITE_URL || null, - API_PREFIX: process.env.APP_API_PREFIX || '/api', - AUTH_TOKEN_KEY: process.env.AUTH_TOKEN_KEY || 'ai-chat-token', + API_PREFIX: process.env.APP_API_PREFIX || '/mgmt/v1/metahuman', + AUTH_TOKEN_KEY: process.env.AUTH_TOKEN_KEY || 'digital-person-token', + AUTHED_PERSON_DATA_KEY: process.env.AUTHED_PERSON_DATA_KEY || 'digital-person-user-info', }), AppMode: JSON.stringify(mode) }, @@ -30,11 +31,11 @@ export default defineConfig(({mode}) => { server: { port: 10021, proxy: { - '/query': { - target: 'http://101.132.145.21:606', // - changeOrigin: true, - ws: true, - //rewrite: (path) => path.replace(/^\/api/, '') + '/mgmt': { + target: 'http://192.168.0.231:9999', //\ + // changeOrigin: true, + // ws: true, + // rewrite: (path) => path.replace(/^\/api/, '') } } }