CommonContent.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. import { DataModel, transformArrayDataModel, type NewDataModel } from '@imengyu/js-request-transform';
  2. import { AppServerRequestModule } from './RequestModules';
  3. import { transformSomeToArray } from '@/api/Utils';
  4. import type { QueryParams } from "@imengyu/imengyu-utils";
  5. export class GetColumListParams extends DataModel<GetColumListParams> {
  6. public constructor() {
  7. super(GetColumListParams);
  8. this.setNameMapperCase('Camel', 'Snake');
  9. }
  10. setModelId(val: number) {
  11. this.modelId = val;
  12. return this;
  13. }
  14. setMainBodyColumnId(val: number) {
  15. this.mainBodyColumnId = val;
  16. return this;
  17. }
  18. setFlag(val: 'hot'|'recommend'|'top') {
  19. this.flag = val;
  20. return this;
  21. }
  22. setSize(val: number) {
  23. this.size = val;
  24. return this;
  25. }
  26. modelId?: number;
  27. /**
  28. * 主体栏目id
  29. */
  30. mainBodyColumnId: number = 0;
  31. /**
  32. * 标志:hot=热门,recommend=推荐,top=置顶
  33. */
  34. flag ?: 'hot'|'recommend'|'top';
  35. /**
  36. * 内容数量,默认4
  37. */
  38. size = 4;
  39. }
  40. export class GetContentListParams extends DataModel<GetContentListParams> {
  41. public constructor() {
  42. super(GetContentListParams);
  43. this.setNameMapperCase('Camel', 'Snake');
  44. this._convertTable = {
  45. ids: {
  46. customToServerFn: (val) => (val as number[]).join(','),
  47. customToClientFn: (val) => (val as string).split(',').map((item) => parseInt(item)),
  48. },
  49. }
  50. }
  51. setMainBodyColumnId(val: number|number[]) {
  52. this.mainBodyColumnId = val;
  53. return this;
  54. }
  55. setFlag(val: 'hot'|'recommend'|'top') {
  56. this.flag = val;
  57. return this;
  58. }
  59. setIds(val: number[]) {
  60. this.ids = val;
  61. return this;
  62. }
  63. setType(val: 1|2|3|4) {
  64. this.type = val;
  65. return this;
  66. }
  67. setSize(val: number) {
  68. this.size = val;
  69. return this;
  70. }
  71. setKeywords(val: string) {
  72. this.keywords = val;
  73. return this;
  74. }
  75. setModelId(val: number) {
  76. this.modelId = val;
  77. return this;
  78. }
  79. static TYPE_ARTICLE = 1;
  80. static TYPE_AUDIO = 2;
  81. static TYPE_VIDEO = 3;
  82. static TYPE_IMAGE = 4;
  83. modelId ?: number;
  84. /**
  85. * 主体栏目id
  86. */
  87. mainBodyColumnId: number|number[] = 0;
  88. /**
  89. * 标志:hot=热门,recommend=推荐,top=置顶
  90. */
  91. flag ?: 'hot'|'recommend'|'top';
  92. /**
  93. * 内容id(逗号隔开)如:3 或者 1,2,3
  94. */
  95. ids?: number[];
  96. /**
  97. * 类型:1=文章,2=音频,3=视频,4=相册
  98. */
  99. type?: 1|2|3|4;
  100. /**
  101. * 内容数量,默认4
  102. */
  103. size = 4;
  104. /**
  105. * 关键字查询
  106. */
  107. keywords?: string;
  108. }
  109. export class GetColumContentList extends DataModel<GetColumContentList> {
  110. constructor() {
  111. super(GetColumContentList, "主体栏目列表");
  112. this.setNameMapperCase('Camel', 'Snake');
  113. this._convertTable = {
  114. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  115. name: { clientSide: 'string', serverSide: 'string', clientSideRequired: true },
  116. content_list: {
  117. clientSide: 'array',
  118. clientSideRequired: true,
  119. clientSideChildDataModel: GetContentListItem,
  120. },
  121. }
  122. }
  123. name = '';
  124. overview = '';
  125. }
  126. export class GetContentListItem extends DataModel<GetContentListItem> {
  127. constructor() {
  128. super(GetContentListItem, "内容列表");
  129. this.setNameMapperCase('Camel', 'Snake');
  130. this._convertTable = {
  131. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  132. mainBodyColumnId: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  133. title: { clientSide: 'string', serverSide: 'string', clientSideRequired: true },
  134. isGuest: { clientSide: 'boolean', serverSide: 'number' },
  135. isLogin: { clientSide: 'boolean', serverSide: 'number' },
  136. isComment: { clientSide: 'boolean', serverSide: 'number' },
  137. isLike: { clientSide: 'boolean', serverSide: 'number' },
  138. isCollect: { clientSide: 'boolean', serverSide: 'number' },
  139. latitude: { clientSide: 'number', serverSide: 'number' },
  140. longitude: { clientSide: 'number', serverSide: 'number' },
  141. publishAt: { clientSide: 'date', serverSide: 'string' },
  142. flag: { clientSide: 'splitCommaArray', serverSide: 'commaArrayMerge' },
  143. tags: { clientSide: 'splitCommaArray', serverSide: 'commaArrayMerge' },
  144. keywords: { clientSide: 'splitCommaArray', serverSide: 'commaArrayMerge' },
  145. type: { clientSide: 'number', serverSide: 'number' },
  146. };
  147. this._convertKeyType = (key, direction) => {
  148. if (key.endsWith('Time') || key.endsWith('At'))
  149. return {
  150. clientSide: 'date',
  151. serverSide: 'string',
  152. };
  153. return undefined;
  154. };
  155. }
  156. id = 0;
  157. mainBodyColumnId = 0;
  158. latitude = 0;
  159. longitude = 0;
  160. mapX = '';
  161. mapY = '';
  162. from = '';
  163. modelId = 0;
  164. title = '!title';
  165. typeText = '';
  166. region = 0;
  167. image = '';
  168. thumbnail = '';
  169. desc = '!desc';
  170. content = '!content';
  171. type = 0;
  172. keywords ?: string[];
  173. flag ?: string[];
  174. tags ?: string[];
  175. views = 0;
  176. comments = 0;
  177. likes = 0;
  178. collects = 0;
  179. dislikes = 0;
  180. district = '';
  181. publishAt = new Date();
  182. }
  183. export class GetContentDetailItem extends DataModel<GetContentDetailItem> {
  184. constructor() {
  185. super(GetContentDetailItem, "内容详情");
  186. this.setNameMapperCase('Camel', 'Snake');
  187. this._convertTable = {
  188. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  189. title: { clientSide: 'string', serverSide: 'string' },
  190. content: { clientSide: 'string', serverSide: 'string' },
  191. isGuest: { clientSide: 'boolean', serverSide: 'number' },
  192. isLogin: { clientSide: 'boolean', serverSide: 'number' },
  193. isComment: { clientSide: 'boolean', serverSide: 'number' },
  194. isLike: { clientSide: 'boolean', serverSide: 'number' },
  195. isCollect: { clientSide: 'boolean', serverSide: 'number' },
  196. publishAt: { clientSide: 'date', serverSide: 'string' },
  197. flag: { clientSide: 'splitCommaArray', serverSide: 'commaArrayMerge' },
  198. tags: { clientSide: 'splitCommaArray', serverSide: 'commaArrayMerge' },
  199. type: { clientSide: 'number', serverSide: 'number' },
  200. ichSitesList: { clientSide: 'array', clientSideChildDataModel: GetContentDetailItem },
  201. inheritorsList: { clientSide: 'array', clientSideChildDataModel: GetContentDetailItem },
  202. otherLevel: { clientSide: 'array', clientSideChildDataModel: GetContentDetailItem },
  203. }
  204. this._convertKeyType = (key, direction) => {
  205. if (key.endsWith('Time') || key.endsWith('At'))
  206. return {
  207. clientSide: 'date',
  208. serverSide: 'string',
  209. };
  210. else if (key.endsWith('List')) {
  211. return [
  212. { clientSide: 'map', serverSide: 'original'},
  213. { clientSide: 'array', clientSideChildDataModel: GetContentDetailItem, serverSide: 'original' },
  214. ]
  215. }
  216. return undefined;
  217. };
  218. this._afterSolveServer = () => {
  219. if (this.image === 'https://mncdn.wenlvti.net')
  220. this.image = '';
  221. if (!this.image && this.images && this.images && this.images.length > 0 ) {
  222. this.image = this.images[0]
  223. }
  224. if ((!this.images || this.images.length == 0) && this.image && this.image != '') {
  225. this.images = [ this.image ]
  226. }
  227. if (!this.images)
  228. this.images = []
  229. }
  230. }
  231. id = 0;
  232. from = '';
  233. modelId = 0;
  234. type = 0;
  235. title = '';
  236. region = 0;
  237. image = '';
  238. images = [] as string[];
  239. audio = '';
  240. video = '';
  241. desc = '';
  242. flag ?: string[];
  243. tags ?: string[];
  244. views = 0;
  245. comments = 0;
  246. likes = 0;
  247. collects = 0;
  248. dislikes = 0;
  249. isLogin = false;
  250. isGuest = false;
  251. isComment = false;
  252. isLike = false;
  253. isCollect = false;
  254. content = '';
  255. value = '';
  256. intro = '';
  257. publishAt = new Date();
  258. associationMeList = [] as {
  259. id: number,
  260. title: string,
  261. image: string,
  262. thumbnail: string,
  263. }[];
  264. otherLevel : GetContentDetailItem[] = [];
  265. }
  266. export class CategoryListItem extends DataModel<CategoryListItem> {
  267. constructor() {
  268. super(CategoryListItem, "分类列表");
  269. this.setNameMapperCase('Camel', 'Snake');
  270. this._convertTable = {
  271. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  272. pid: { clientSide: 'number', serverSide: 'number' },
  273. haschild: { clientSide: 'boolean', serverSide: 'number' },
  274. }
  275. }
  276. id !: number;
  277. pid !: number;
  278. title = '';
  279. status = 'normal';
  280. weight = 0;
  281. spacer = '';
  282. haschild = false;
  283. children?: CategoryListItem[];
  284. }
  285. export class CommonContentApi extends AppServerRequestModule<DataModel> {
  286. constructor(modelId: number, debugName: string, mainBodyColumnId?: number|number[]) {
  287. super();
  288. this.modelId = modelId;
  289. this.mainBodyColumnId = mainBodyColumnId;
  290. this.debugName = debugName;
  291. }
  292. public mainBodyColumnId?: number|number[];
  293. public modelId: number;
  294. protected debugName: string;
  295. /**
  296. * 获取分类列表
  297. * @param type 根级类型:1=区域、2=级别、3=文物类型、4=非遗类型、42=事件类型
  298. * @param withself 是否返回包含自己:true=是,false=否 ,默认false
  299. * @returns
  300. */
  301. async getCategoryList(
  302. type?: number,
  303. withself?: boolean,
  304. ) {
  305. if (type === 1)
  306. withself = true;
  307. return (this.get('/content/category/getCategoryList', '获取分类列表', {
  308. type,
  309. is_tree: false,
  310. withself,
  311. }))
  312. .then(res => transformArrayDataModel<CategoryListItem>(CategoryListItem, res.data2, `获取分类列表`, true))
  313. .catch(e => { throw e });
  314. }
  315. /**
  316. * 用于获取某一个分类需要用的子级
  317. * @param pid 父级
  318. * @returns
  319. */
  320. async getCategoryChildList(pid?: number) {
  321. return (this.get('/content/category/getCategoryOnlyChildList', '获取分类子级列表', {
  322. pid,
  323. }))
  324. .then(res => transformArrayDataModel<CategoryListItem>(
  325. CategoryListItem,
  326. transformSomeToArray(res.data2),
  327. `获取分类列表`,
  328. true
  329. ))
  330. .catch(e => { throw e });
  331. }
  332. private toStringArray(arr: number|number[]|undefined) {
  333. if (typeof arr === 'undefined')
  334. return '';
  335. return typeof arr === 'object' ? arr.join(',') : arr.toString();
  336. }
  337. /**
  338. * 主体栏目列表
  339. * @param params 参数
  340. * @param querys 额外参数
  341. * @returns
  342. */
  343. getColumList<T extends DataModel = GetColumContentList>(params: GetColumListParams, modelClassCreator: NewDataModel = GetColumContentList, querys?: QueryParams) {
  344. return this.get('/content/content/getMainBodyColumnContentList', `${this.debugName} 主体栏目列表`, {
  345. model_id: this.modelId,
  346. ...params.toServerSide(),
  347. ...querys,
  348. })
  349. .then(res => ({
  350. list: transformArrayDataModel<T>(modelClassCreator, res.data2.list, `${this.debugName} 主体栏目列表`, true),
  351. total: res.data2.total as number,
  352. }))
  353. .catch(e => { throw e });
  354. }
  355. /**
  356. * 模型内容列表
  357. * @param params 参数
  358. * @param page 页码
  359. * @param pageSize 页大小
  360. * @param querys 额外参数
  361. * @returns
  362. */
  363. getContentList<T extends DataModel = GetContentListItem>(params: GetContentListParams, page: number, pageSize: number = 10, modelClassCreator: NewDataModel = GetContentListItem, querys?: QueryParams) {
  364. return this.get('/content/content/getContentList', `${this.debugName} 模型内容列表`, {
  365. ...params.toServerSide(),
  366. ...querys,
  367. model_id: params.modelId || this.modelId,
  368. main_body_column_id: this.toStringArray(params.mainBodyColumnId || this.mainBodyColumnId),
  369. page,
  370. pageSize,
  371. })
  372. .then(res => ({
  373. list: transformArrayDataModel<T>(modelClassCreator, res.data2.list, `${this.debugName} 模型内容列表`, true),
  374. total: res.data2.total as number,
  375. }))
  376. .catch(e => { throw e });
  377. }
  378. /**
  379. * 内容详情
  380. * @param id id
  381. * @param querys 额外参数
  382. * @returns
  383. */
  384. getContentDetail<T extends DataModel = GetContentDetailItem>(id: number, modelId?: number, modelClassCreator: NewDataModel = GetContentDetailItem, querys?: QueryParams) {
  385. return this.get('/content/content/getContentDetail', `${this.debugName} (${id}) 内容详情`, {
  386. model_id: modelId ?? this.modelId,
  387. id,
  388. ...querys,
  389. }, modelClassCreator)
  390. .then(res => res.data as T)
  391. .catch(e => { throw e });
  392. }
  393. /**
  394. * 上传文件到服务器
  395. */
  396. async uploadSmallFile(
  397. file: File,
  398. fileType?: "image" | "video" | "audio" | undefined,
  399. name = 'file',
  400. data?: Record<string, any>
  401. ) {
  402. return new Promise<{
  403. fullurl: string,
  404. url: string
  405. }>(async (resolve, reject) => {
  406. try {
  407. let url = this.config.baseUrl + '/common/upload';
  408. const formData = new FormData();
  409. formData.append(name, file);
  410. // 添加额外数据
  411. if (data) {
  412. Object.entries(data).forEach(([key, value]) => {
  413. formData.append(key, value);
  414. });
  415. }
  416. let requestOptions: RequestInit = {
  417. method: 'POST',
  418. body: formData,
  419. headers: {}
  420. };
  421. // 应用请求拦截器
  422. if (this.config.requestInceptor) {
  423. const { newReq, newUrl } = this.config.requestInceptor(url, requestOptions as any);
  424. url = newUrl;
  425. requestOptions = newReq as RequestInit;
  426. }
  427. // 移除Content-Type,让浏览器自动处理
  428. if (requestOptions.headers && (requestOptions.headers as Record<string, string>)['Content-Type'])
  429. delete (requestOptions.headers as Record<string, string>)['Content-Type'];
  430. const response = await fetch(url, requestOptions);
  431. const responseData = await response.json();
  432. if (!response.ok)
  433. throw new Error(`HTTP error! status: ${response.status}`);
  434. if (responseData.code !== 1)
  435. throw new Error(responseData.msg ?? `code: ${responseData.code}`);
  436. resolve(responseData.data);
  437. } catch (error) {
  438. reject(error);
  439. }
  440. });
  441. }
  442. }
  443. export default new CommonContentApi(0, '默认通用内容');