add tag link
This commit is contained in:
parent
64f5c82c8c
commit
e63e82fde9
@ -11,8 +11,8 @@ export const toQueryString = (obj:ToQueryStringA)=> {
|
|||||||
e[1] !== undefined)
|
e[1] !== undefined)
|
||||||
.map(e =>
|
.map(e =>
|
||||||
e[1] instanceof Array
|
e[1] instanceof Array
|
||||||
? e[1].map(f=>`${e[0]}[]=${encodeURIComponent(f)}`).join('&')
|
? e[1].map(f=>`${e[0]}=${(f)}`).join('&')
|
||||||
: `${e[0]}=${encodeURIComponent(e[1])}`)
|
: `${e[0]}=${(e[1])}`)
|
||||||
.join('&');
|
.join('&');
|
||||||
}
|
}
|
||||||
export const QueryStringToMap = (query:string) =>{
|
export const QueryStringToMap = (query:string) =>{
|
||||||
@ -20,16 +20,16 @@ export const QueryStringToMap = (query:string) =>{
|
|||||||
const param:{[k:string]:string|string[]} = {};
|
const param:{[k:string]:string|string[]} = {};
|
||||||
keyValue.forEach((p)=>{
|
keyValue.forEach((p)=>{
|
||||||
const [k,v] = p.split("=");
|
const [k,v] = p.split("=");
|
||||||
if(k.endsWith("[]")){
|
const pv = param[k];
|
||||||
let arr = param[k];
|
if(pv === undefined){
|
||||||
if(arr instanceof Array){
|
param[k] = v;
|
||||||
arr.push(v);
|
}
|
||||||
|
else if(typeof pv === "string"){
|
||||||
|
param[k] = [pv,v];
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
param[k] = [v];
|
pv.push(v);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else param[k] = v;
|
|
||||||
});
|
});
|
||||||
return param;
|
return param;
|
||||||
}
|
}
|
@ -117,7 +117,7 @@ export const ContentInfo = (props: {
|
|||||||
</Link>
|
</Link>
|
||||||
<Box className={propclasses.subinfoContainer || subinfoContainer}>
|
<Box className={propclasses.subinfoContainer || subinfoContainer}>
|
||||||
{props.short ? (<Box className={propclasses.tag_list || classes.tag_list}>{document.tags.map(x =>
|
{props.short ? (<Box className={propclasses.tag_list || classes.tag_list}>{document.tags.map(x =>
|
||||||
(<TagChip key={x} label={x} clickable={true} tagname={x} size="small"></TagChip>)
|
(<TagChip key={x} label={x} clickable tagname={x} size="small"></TagChip>)
|
||||||
)}</Box>) : (
|
)}</Box>) : (
|
||||||
<>
|
<>
|
||||||
<Typography variant='subtitle1'>Artist</Typography>
|
<Typography variant='subtitle1'>Artist</Typography>
|
||||||
@ -125,7 +125,7 @@ export const ContentInfo = (props: {
|
|||||||
<Typography variant='subtitle1'>Tags</Typography>
|
<Typography variant='subtitle1'>Tags</Typography>
|
||||||
<Box className={propclasses.tag_list || classes.tag_list}>
|
<Box className={propclasses.tag_list || classes.tag_list}>
|
||||||
{allTag.map(x => {
|
{allTag.map(x => {
|
||||||
return (<TagChip key={x} label={x} clickable={true} tagname={x} size="small"></TagChip>);
|
return (<TagChip key={x} label={x} clickable tagname={x} size="small"></TagChip>);
|
||||||
})}
|
})}
|
||||||
</Box>
|
</Box>
|
||||||
</>)
|
</>)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {ChipProps} from '@material-ui/core/Chip';
|
import {ChipTypeMap} from '@material-ui/core/Chip';
|
||||||
import { Chip, colors } from '@material-ui/core';
|
import { Chip, colors } from '@material-ui/core';
|
||||||
import {useTheme, makeStyles, Theme, emphasize, fade} from '@material-ui/core/styles';
|
import {useTheme, makeStyles, Theme, emphasize, fade} from '@material-ui/core/styles';
|
||||||
|
import {Link as RouterLink} from 'react-router-dom';
|
||||||
|
|
||||||
type TagChipStyleProp = {
|
type TagChipStyleProp = {
|
||||||
color: string
|
color: string
|
||||||
@ -43,14 +44,20 @@ const useTagStyles = makeStyles((theme:Theme)=>({
|
|||||||
const {blue, pink} = colors;
|
const {blue, pink} = colors;
|
||||||
const getTagColorName = (tagname :string):string=>{
|
const getTagColorName = (tagname :string):string=>{
|
||||||
if(tagname.startsWith("female")){
|
if(tagname.startsWith("female")){
|
||||||
return pink['700'];
|
return pink[600];
|
||||||
}
|
}
|
||||||
else if(tagname.startsWith("male")){
|
else if(tagname.startsWith("male")){
|
||||||
return blue[600];
|
return blue[600];
|
||||||
}
|
}
|
||||||
else return "default";
|
else return "default";
|
||||||
}
|
}
|
||||||
export const ColorChip = (props:Omit<ChipProps,"color"> & {color: string})=>{
|
|
||||||
|
type ColorChipProp = Omit<ChipTypeMap['props'],"color"> & TagChipStyleProp & {
|
||||||
|
component?: React.ElementType,
|
||||||
|
to?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ColorChip = (props:ColorChipProp)=>{
|
||||||
const {color,...rest} = props;
|
const {color,...rest} = props;
|
||||||
const classes = useTagStyles({color : color !== "default" ? color : "#000"});
|
const classes = useTagStyles({color : color !== "default" ? color : "#000"});
|
||||||
return <Chip color="default" classes={{
|
return <Chip color="default" classes={{
|
||||||
@ -58,8 +65,12 @@ export const ColorChip = (props:Omit<ChipProps,"color"> & {color: string})=>{
|
|||||||
}} {...rest}></Chip>;
|
}} {...rest}></Chip>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TagChip = (props:Omit<ChipProps,"color"> & {tagname: string})=>{
|
type TagChipProp = Omit<ChipTypeMap['props'],"color"> & {
|
||||||
const {tagname,label,...rest} = props;
|
tagname:string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TagChip = (props:TagChipProp)=>{
|
||||||
|
const {tagname,label,clickable,...rest} = props;
|
||||||
let newlabel:string|undefined = undefined;
|
let newlabel:string|undefined = undefined;
|
||||||
if(typeof label === "string"){
|
if(typeof label === "string"){
|
||||||
if(label.startsWith("female:")){
|
if(label.startsWith("female:")){
|
||||||
@ -69,5 +80,9 @@ export const TagChip = (props:Omit<ChipProps,"color"> & {tagname: string})=>{
|
|||||||
newlabel = "♂ "+label.slice(5);
|
newlabel = "♂ "+label.slice(5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return <ColorChip color={getTagColorName(tagname)} label={newlabel||label} {...rest}></ColorChip>;
|
const inner = clickable ?
|
||||||
|
(<ColorChip color={getTagColorName(tagname)} clickable={clickable} label={newlabel||label} {...rest}
|
||||||
|
component={RouterLink} to={`/search?allow_tag=${tagname}`}></ColorChip>):
|
||||||
|
(<ColorChip color={getTagColorName(tagname)} clickable={clickable} label={newlabel||label} {...rest}></ColorChip>);
|
||||||
|
return inner;
|
||||||
}
|
}
|
@ -80,12 +80,16 @@ class KnexDocumentAccessor implements DocumentAccessor{
|
|||||||
const cursor = option.cursor;
|
const cursor = option.cursor;
|
||||||
|
|
||||||
const buildquery = ()=>{
|
const buildquery = ()=>{
|
||||||
let query = this.knex.select("*");
|
let query = this.knex.select("document.*");
|
||||||
if(allow_tag.length > 0){
|
if(allow_tag.length > 0){
|
||||||
query = query.from("doc_tag_relation").innerJoin("document","doc_tag_relation.doc_id","document.id");
|
query = query.from("doc_tag_relation as tags_0");
|
||||||
for(const tag of allow_tag){
|
query = query.where("tags_0.tag_name","=",allow_tag[0]);
|
||||||
query = query.where({tag_name:tag});
|
for (let index = 1; index < allow_tag.length; index++) {
|
||||||
|
const element = allow_tag[index];
|
||||||
|
query = query.innerJoin(`doc_tag_relation as tags_${index}`,`tags_${index}.doc_id`,"tags_0.doc_id");
|
||||||
|
query = query.where(`tags_${index}.tag_name`,'=',element);
|
||||||
}
|
}
|
||||||
|
query = query.innerJoin("document","tags_0.doc_id","document.id");
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
query = query.from("document");
|
query = query.from("document");
|
||||||
|
@ -2,7 +2,7 @@ import { Context, Next } from 'koa';
|
|||||||
import Router from 'koa-router';
|
import Router from 'koa-router';
|
||||||
import {Document, DocumentAccessor, isDocBody} from '../model/doc';
|
import {Document, DocumentAccessor, isDocBody} from '../model/doc';
|
||||||
import {QueryListOption} from '../model/doc';
|
import {QueryListOption} from '../model/doc';
|
||||||
import {ParseQueryNumber, ParseQueryArray, ParseQueryBoolean} from './util'
|
import {ParseQueryNumber, ParseQueryArray, ParseQueryBoolean, ParseQueryArgString} from './util'
|
||||||
import {sendError} from './error_handler';
|
import {sendError} from './error_handler';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import {AllContentRouter} from './all';
|
import {AllContentRouter} from './all';
|
||||||
@ -31,14 +31,14 @@ const ContentTagIDHandler = (controller: DocumentAccessor) => async (ctx: Contex
|
|||||||
const ContentQueryHandler = (controller : DocumentAccessor) => async (ctx: Context,next: Next)=>{
|
const ContentQueryHandler = (controller : DocumentAccessor) => async (ctx: Context,next: Next)=>{
|
||||||
const limit = ParseQueryNumber(ctx.query['limit']);
|
const limit = ParseQueryNumber(ctx.query['limit']);
|
||||||
const cursor = ParseQueryNumber(ctx.query['cursor']);
|
const cursor = ParseQueryNumber(ctx.query['cursor']);
|
||||||
const word: string|undefined = ctx.query['word'];
|
const word = ParseQueryArgString(ctx.query['word']);
|
||||||
const content_type:string|undefined = ctx.query['content_type'];
|
const content_type = ParseQueryArgString(ctx.query['content_type']);
|
||||||
const offset = ParseQueryNumber(ctx.query['offset']);
|
const offset = ParseQueryNumber(ctx.query['offset']);
|
||||||
if(limit === NaN || cursor === NaN || offset === NaN){
|
if(limit === NaN || cursor === NaN || offset === NaN){
|
||||||
return sendError(400,"parameter limit, cursor or offset is not a number");
|
return sendError(400,"parameter limit, cursor or offset is not a number");
|
||||||
}
|
}
|
||||||
const allow_tag = ParseQueryArray(ctx.query['allow_tag[]']);
|
const allow_tag = ParseQueryArray(ctx.query['allow_tag']);
|
||||||
let [ok,use_offset] = ParseQueryBoolean(ctx.query['use_offset']);
|
const [ok,use_offset] = ParseQueryBoolean(ctx.query['use_offset']);
|
||||||
if(!ok){
|
if(!ok){
|
||||||
return sendError(400,"use_offset must be true or false.");
|
return sendError(400,"use_offset must be true or false.");
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ const ContentQueryHandler = (controller : DocumentAccessor) => async (ctx: Conte
|
|||||||
eager_loading: true,
|
eager_loading: true,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
use_offset: use_offset,
|
use_offset: use_offset,
|
||||||
content_type:content_type,
|
content_type: content_type,
|
||||||
};
|
};
|
||||||
let document = await controller.findList(option);
|
let document = await controller.findList(option);
|
||||||
ctx.body = document;
|
ctx.body = document;
|
||||||
|
@ -8,7 +8,11 @@ export function ParseQueryNumber(s: string|undefined): number| undefined{
|
|||||||
}
|
}
|
||||||
export function ParseQueryArray(s: string[]|string|undefined){
|
export function ParseQueryArray(s: string[]|string|undefined){
|
||||||
s = s || [];
|
s = s || [];
|
||||||
return s instanceof Array ? s : [s];
|
const r = s instanceof Array ? s : [s];
|
||||||
|
return r.map(x=>decodeURIComponent(x));
|
||||||
|
}
|
||||||
|
export function ParseQueryArgString(s: string|undefined){
|
||||||
|
return s === undefined ? s : decodeURIComponent(s);
|
||||||
}
|
}
|
||||||
export function ParseQueryBoolean(s: string|undefined): [boolean,boolean|undefined]{
|
export function ParseQueryBoolean(s: string|undefined): [boolean,boolean|undefined]{
|
||||||
let value:boolean|undefined;
|
let value:boolean|undefined;
|
||||||
|
Loading…
Reference in New Issue
Block a user