lodash#has TypeScript Examples
The following examples show how to use
lodash#has.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts From ui5-language-assistant with Apache License 2.0 | 7 votes |
export function findValueInMaps<T>(
key: string,
...maps: Record<string, T>[]
): T | undefined {
for (const mapElement of maps) {
if (has(mapElement, key)) {
return mapElement[key];
}
}
return undefined;
}
Example #2
Source File: utils.ts From redux-with-domain with MIT License | 7 votes |
// get state by path
// parent module's data aside in .base
export function getStateByNamespace(state, namespace, initialState) {
const path = toStorePath(namespace)
const initialStateKeys = keys(initialState)
const findState = get(state, path)
if (findState === undefined) {
throw Error(`Please check if you forget to add module ${path} `)
}
if (isEmpty(initialState)) {
if (findState['@@loading']) {
return findState
}
return get(state, `${path}.base`) // not in base
}
let isModuleState = true
initialStateKeys.forEach(key => {
if (!has(findState, key)) {
isModuleState = false
}
})
if (isModuleState) return findState
return get(state, `${path}.base`)
}
Example #3
Source File: pivot-data-set.ts From S2 with MIT License | 6 votes |
public getTotalStatus = (query: DataType) => {
const { columns, rows } = this.fields;
const isTotals = (dimensions: string[], isSubTotal?: boolean) => {
if (isSubTotal) {
const firstDimension = find(dimensions, (item) => !has(query, item));
return firstDimension && firstDimension !== first(dimensions);
}
return every(dimensions, (item) => !has(query, item));
};
const getDimensions = (dimensions: string[], hasExtra: boolean) => {
return hasExtra
? dimensions.filter((item) => item !== EXTRA_FIELD)
: dimensions;
};
return {
isRowTotal: isTotals(
getDimensions(rows, !this.spreadsheet.isValueInCols()),
),
isRowSubTotal: isTotals(rows, true),
isColTotal: isTotals(
getDimensions(columns, this.spreadsheet.isValueInCols()),
),
isColSubTotal: isTotals(columns, true),
};
};
Example #4
Source File: packet.utils.ts From nestjs-jaeger-tracing with MIT License | 6 votes |
export function isRequestPacket<T>(
packet: unknown,
): packet is RequestPacket<T> {
const keys: Array<keyof RequestPacket<T>> = ['id', 'data', 'pattern'];
return every(keys, partial(has, packet));
}
Example #5
Source File: get-right-field-in-query.ts From S2 with MIT License | 6 votes |
export function getRightFieldInQuery(
rowQuery: Record<string, any>,
rowFields: string[],
): string {
let field = '';
for (let k = rowFields.length - 1; k >= 0; k--) {
if (has(rowQuery, rowFields[k])) {
field = rowFields[k]; // 行头中的维度
break;
}
}
return field;
}
Example #6
Source File: global-secondary-index.ts From dyngoose with ISC License | 6 votes |
/**
* Performs a query and returns the first item matching your filters.
*
* The regular DynamoDB.GetItem does not work for indexes, because DynamoDB only enforces a
* unique cross of the tale's primary HASH and RANGE key. The combination of those two values
* must always be unique, however, for a GlobalSecondaryIndex, there is no uniqueness checks
* or requirements. This means that querying for a record by the HASH and RANGE on a
* GlobalSecondaryIndex it is always possible there are multiple matching records.
*
* So use this with caution. It sets the DynamoDB Limit parameter to 1, which means this will
* not work for additional filtering. If you want to provide additional filtering, use the
* .query() method and pass your filters, then handle if the query has more than one result.
*
* Avoid use whenever you do not have uniqueness for the GlobalSecondaryIndex's HASH + RANGE.
*/
public async get(filters: QueryFilters<T>, input: GlobalSecondaryIndexGetInput = {}): Promise<T | undefined> {
if (!has(filters, this.metadata.hash.propertyName)) {
throw new QueryError('Cannot perform .get() on a GlobalSecondaryIndex without specifying a hash key value')
} else if (this.metadata.range != null && !has(filters, this.metadata.range.propertyName)) {
throw new QueryError('Cannot perform .get() on a GlobalSecondaryIndex without specifying a range key value')
} else if (Object.keys(filters).length > 2) {
throw new QueryError('Cannot perform a .get() on a GlobalSecondaryIndex with additional filters, use .query() instead')
}
(input as GlobalSecondaryIndexQueryInput).limit = 1
// because you are specifying the hashKey and rangeKey, we can apply a limit to this search
// DynamoDB will start the search at the first match and limit means it will only process
// that document and return it, however, you cannot use any additional filters on this .get
// method; for that, you need to use .query()
const results = await this.query(filters, input)
if (results.count > 0) {
return results[0]
}
}
Example #7
Source File: api.ts From brick-design with MIT License | 6 votes |
function responseAdaptor(ret: any, api: ApiObject) {
let hasStatusField = true;
if (!ret) {
throw new Error('Response is empty!');
} else if (!has(ret, 'status')) {
hasStatusField = false;
}
const result = ret.data || ret.result;
const payload: Payload = {
ok: hasStatusField === false || ret.status == 0,
status: hasStatusField === false ? 0 : ret.status,
msg: ret.msg || ret.message,
msgTimeout: ret.msgTimeout,
data: Array.isArray(result) ? { items: result } : result, // 兼容直接返回数据的情况
isNotState: api.isNotState,
isPageState: api.isPageState,
};
if (payload.status == 422) {
payload.errors = ret.errors;
}
if (payload.ok && api.responseData) {
payload.data = dataMapping(
api.responseData,
createObject({ api }, payload.data || {}),
);
}
return payload;
}
Example #8
Source File: packet.utils.ts From nestjs-jaeger-tracing with MIT License | 6 votes |
export function isTracingContext(packet: unknown): packet is TracingContext {
const keys: Array<keyof TracingContext> = ['payload', 'isSerialized'];
return every(keys, partial(has, packet));
}
Example #9
Source File: fixture.ts From ts-di-starter with MIT License | 6 votes |
checkFixture = (fixture, expected): void => {
const { expressMiddleware, expressControllers } = fixture;
const updateExpectationsFromFixture = (fixtureObj, expectedObj) => {
return Object.keys(fixtureObj).reduce((_expectedObj, key) => {
const isStub = has(fixtureObj[key], 'callsFake');
if (isStub) {
fixtureObj[key] = fixtureObj[key].callCount;
}
if (!has(_expectedObj, key)) {
if (isStub) {
_expectedObj[key] = 0;
} else {
_expectedObj[key] = {};
}
}
if (isObject(fixtureObj[key])) {
updateExpectationsFromFixture(fixtureObj[key], _expectedObj[key]);
}
return _expectedObj;
}, expectedObj);
};
updateExpectationsFromFixture(expressControllers, expected.controllers);
updateExpectationsFromFixture(expressMiddleware, expected.middleware);
expect(expressControllers).to.deep.equal(expected.controllers);
expect(expressMiddleware).to.deep.equal(expected.middleware);
}
Example #10
Source File: external-item.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
getSelectOptions = (options: Option[], filterKey: string) => {
if (!filterKey) return options;
const useableOptions: Option[] = [];
options.forEach((item) => {
let curOp: Option | null = null;
if (has(item, 'children')) {
curOp = { ...item, children: [] };
item.children?.forEach((cItem) => {
if (filterMatch(`${cItem.label}`, filterKey)) {
curOp?.children?.push(cItem);
}
});
if (curOp.children?.length) useableOptions.push(curOp);
} else if (filterMatch(`${item.label}`, filterKey)) {
curOp = item;
curOp && useableOptions.push(curOp);
}
});
return useableOptions;
}
Example #11
Source File: util.ts From gio-design with Apache License 2.0 | 6 votes |
dimensionToPropertyItem: TypeMapping = (item: Dimension, localeText: typeof defaultLocale) => {
const result: PropertyItem = { label: item.name, value: item.id, ...item };
const { id, groupId, type: _type, associatedKey } = item;
result.iconId = groupId;
if (groupId === 'normal' && _type === 'global') {
const [newGroupId, newGroupName] = PreparedNormalDimensionIdMap(id, localeText);
result.groupId = newGroupId;
result.groupName = newGroupName;
result.iconId = newGroupId;
}
const dimensionGroupType = DimensionGroupTypeMapper[result.groupId ?? 'event'];
result.previewTypeName = PropertyTypes(localeText)[dimensionGroupType === 'avar' ? 'event' : dimensionGroupType];
// 多物品模型,物品属性不再作为事件绑定属性,而是作为事件属性的属性来展示,作为事件属性的子集
// 所以,当存在parentId的时候,物品属性,和事件属性同组
if (groupId === 'item' && _type === 'itm' && associatedKey) {
result.iconId = 'item';
result.groupId = 'event';
result.previewTypeName = localeText.dimensionTable;
result.groupName = localeText.eventVariables;
}
// 虚拟属性需要添加到事件属性中,但是有自己的type和groupId,所以和维度表(多物品模型)做相同处理
if (groupId === 'virtual' && _type === 'vvar') {
result.iconId = 'virtual';
result.groupName = localeText.virtualProperties;
result.previewTypeName = localeText.virtualProperties;
}
const gOrder = PropertyGroupOrder.indexOf(result.groupId as string);
result.groupOrder = gOrder > -1 ? gOrder : 99999;
result.type = DimensionGroupTypeMapper[result.groupId || 'unkown'];
result.typeName = PropertyTypes(localeText)[result.type];
const tOrder = ['event', 'avar', 'usr'].indexOf(result.type);
result.typeOrder = tOrder > -1 ? tOrder : 99999;
if (has(item, 'isSystem') && groupId !== 'virtual') {
return regroup(result, localeText);
}
return result;
}
Example #12
Source File: kbn.ts From grafana-chinese with Apache License 2.0 | 6 votes |
kbn.describe_interval = (str: string) => {
const matches = str.match(kbn.interval_regex);
if (!matches || !has(kbn.intervals_in_seconds, matches[2])) {
throw new Error('Invalid interval string, expecting a number followed by one of "Mwdhmsy"');
} else {
return {
sec: kbn.intervals_in_seconds[matches[2]],
type: matches[2],
count: parseInt(matches[1], 10),
};
}
};
Example #13
Source File: semantic-model-provider.ts From ui5-language-assistant with Apache License 2.0 | 6 votes |
export async function downloadLibraries(
version: TestModelVersion
): Promise<void> {
if (!has(downloadedLibrariesPromises, version)) {
downloadedLibrariesPromises[version] = addUi5Resources(
version,
getModelFolder(version)
);
}
return downloadedLibrariesPromises[version];
}
Example #14
Source File: pivot-data-set.ts From S2 with MIT License | 6 votes |
protected standardTransform(originData: Data[], fieldsValues: string[]) {
if (isEmpty(fieldsValues)) {
return originData;
}
const transformedData = [];
forEach(fieldsValues, (value) => {
forEach(originData, (dataItem) => {
if (has(dataItem, value)) {
transformedData.push({
...dataItem,
[EXTRA_FIELD]: value,
[VALUE_FIELD]: dataItem[value],
});
}
});
});
return transformedData;
}
Example #15
Source File: resolve.ts From ui5-language-assistant with Apache License 2.0 | 6 votes |
function fixTypeName(
fqn: string | undefined,
typeNameFix: TypeNameFix
): string | undefined {
if (fqn === undefined || ignoreType(fqn)) {
return undefined;
}
if (has(typeNameFix, fqn)) {
return typeNameFix[fqn];
}
return fqn;
}
Example #16
Source File: operation.ts From openapi-mock-express-middleware with MIT License | 6 votes |
getResponseSchema(responseStatus = 200): JSONSchema | null {
if (
has(this.operation, ['responses', responseStatus, 'content', 'application/json', 'schema'])
) {
const { schema, example, examples } = get(this.operation, [
'responses',
responseStatus,
'content',
'application/json',
]);
if (schema && !isReferenceObject(schema)) {
const resultSchema: JSONSchema = schema as JSONSchema;
if (example) {
resultSchema.example = example;
}
if (examples) {
resultSchema.examples = examples;
}
return resultSchema;
}
return null;
}
return null;
}
Example #17
Source File: analysis-utils.ts From prism-frontend with MIT License | 6 votes |
hasKeys = (obj: any, keys: string[]): boolean =>
!keys.find(key => !has(obj, key))
Example #18
Source File: validateBody.ts From openapi-mock-express-middleware with MIT License | 6 votes |
validateBody = (
req: express.Request,
res: express.Response,
next: express.NextFunction
): void | express.Response => {
if (
!res.locals.operation ||
!(res.locals.operation instanceof Operation) ||
!has(res.locals.operation.operation, 'requestBody')
) {
return next();
}
const bodySchema = res.locals.operation.getBodySchema(
req.get('content-type') || 'application/json'
);
if (Object.keys(req.body).length && !bodySchema) {
return res.status(400).json({
message: 'Bad request. Invalid content type.',
});
}
if (bodySchema) {
const isBodyValid = validator.validate(bodySchema, req.body);
if (!isBodyValid) {
return res.status(400).json({
message: 'Bad request. Invalid request body.',
errors: validator.errors,
});
}
}
return next();
}
Example #19
Source File: pivot-data-set.ts From S2 with MIT License | 5 votes |
public getMultiData(
query: DataType,
isTotals?: boolean,
isRow?: boolean,
drillDownFields?: string[],
): DataType[] {
if (isEmpty(query)) {
return compact(customFlattenDeep(this.indexesData));
}
const { rows, columns, values: valueList } = this.fields;
const totalRows = !isEmpty(drillDownFields)
? rows.concat(drillDownFields)
: rows;
const rowDimensionValues = getQueryDimValues(totalRows, query);
const colDimensionValues = getQueryDimValues(columns, query);
const path = getDataPath({
rowDimensionValues,
colDimensionValues,
careUndefined: true,
isFirstCreate: true,
rowFields: rows,
colFields: columns,
rowPivotMeta: this.rowPivotMeta,
colPivotMeta: this.colPivotMeta,
});
const currentData = this.getCustomData(path);
let result = compact(customFlatten(currentData));
if (isTotals) {
// 总计/小计(行/列)
// need filter extra data
// grand total => {$$extra$$: 'price'}
// sub total => {$$extra$$: 'price', category: 'xxxx'}
// [undefined, undefined, "price"] => [category]
let fieldKeys = [];
const rowKeys = getFieldKeysByDimensionValues(rowDimensionValues, rows);
const colKeys = getFieldKeysByDimensionValues(
colDimensionValues,
columns,
);
if (isRow) {
// 行总计
fieldKeys = rowKeys;
} else {
// 只有一个值,此时为列总计
const isCol = keys(query)?.length === 1 && has(query, EXTRA_FIELD);
if (isCol) {
fieldKeys = colKeys;
} else {
const getTotalStatus = (dimensions: string[]) => {
return isEveryUndefined(
dimensions?.filter((item) => !valueList?.includes(item)),
);
};
const isRowTotal = getTotalStatus(colDimensionValues);
const isColTotal = getTotalStatus(rowDimensionValues);
if (isRowTotal) {
// 行小计
fieldKeys = rowKeys;
} else if (isColTotal) {
// 列小计
fieldKeys = colKeys;
} else {
// 行小计+列 or 列小计+行
fieldKeys = [...rowKeys, ...colKeys];
}
}
}
result = result.filter(
(r) =>
!fieldKeys?.find(
(item) => item !== EXTRA_FIELD && keys(r)?.includes(item),
),
);
}
return result || [];
}
Example #20
Source File: settings-config-spec.ts From ui5-language-assistant with Apache License 2.0 | 5 votes |
describe("settings configuration properties", () => {
let packageJsonSettings: Record<string, Setting>;
before(() => {
// Get the settings from the package.json
const packageJsonPath = require.resolve(
"vscode-ui5-language-assistant/package.json"
);
const packageJsonContent = readJsonSync(packageJsonPath);
packageJsonSettings =
packageJsonContent.contributes.configuration.properties;
});
it("default setting values in package.json have the correct type", () => {
forEach(packageJsonSettings, (value, key) => {
expect(
typeof value.default,
`Setting ${key} default value type does not match the setting's defined type`
).to.equal(value.type);
});
});
it("settings in package.json are in sync with the settings package", () => {
const defaultSettingsFromPackageJson = parseSettings(packageJsonSettings);
const defaultSettings = getDefaultSettings();
expect(
defaultSettingsFromPackageJson,
"settings from package.json don't match the default settings in the language server"
).to.deep.equal({
UI5LanguageAssistant: defaultSettings,
});
});
it("enums in package.json are in sync with the settings package", () => {
const pkgJsonSettingsWithEnum = pickBy(packageJsonSettings, (_) =>
has(_, "enum")
);
forEach(pkgJsonSettingsWithEnum, (pkgJsonSetting, settingsKey) => {
const settingsModulePropName = camelCase(
settingsKey.replace(
/UI5LanguageAssistant.(\w+)\.(\w+)/,
"valid $1 $2 values"
)
);
const settingsModulePropValue = keys(
settingsModule[settingsModulePropName]
);
const pkgJsonPropValue = pkgJsonSetting.enum;
expect(settingsModulePropValue).to.deep.equalInAnyOrder(pkgJsonPropValue);
});
});
it("use the correct logging configuration property name", () => {
expect(packageJsonSettings[LOGGING_LEVEL_CONFIG_PROP]).to.exist;
expect(
packageJsonSettings[LOGGING_LEVEL_CONFIG_PROP].description
).to.include("logging");
});
type Setting = {
scope: string;
type: string;
default: unknown;
description: string;
enum?: string[];
};
function parseSettings(properties: Record<string, Setting>): unknown {
const defaultSettings = {};
forEach(properties, (value, key) => {
set(defaultSettings, key, value.default);
});
return defaultSettings;
}
});
Example #21
Source File: index.tsx From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
PureFilter = (props: IPureFilterProps) => {
const {
filterTirgger = 'onChange',
connectUrlSearch = false,
updateSearch,
onFilter = noop,
query = {},
className = '',
formatFormData,
urlExtra,
...rest
} = props;
// const query = routeInfoStore.getState(s => s.query);
const filterRef: any = React.useRef(null as any);
useMount(() => {
const { pageNo: _, ...fieldsValue } = query;
// 关联url, 将query初始化到表单
if (connectUrlSearch && !isEmpty(fieldsValue)) {
setTimeout(() => {
setFieldsValue(fieldsValue);
}, 0);
} else if (filterTirgger === 'onChange') {
setTimeout(() => {
// 只能在setTimeout中拿到初始请求的值
const formData = filterRef.current?.form.getFieldsValue();
changeFilterData(formData);
}, 0);
}
});
React.useEffect(() => {
if (!isEmpty(urlExtra)) {
const filterForm = get(filterRef, 'current.form');
const filterData = filterForm ? filterForm.getFieldsValue() : {};
updateSearch({ ...urlExtra, ...filterData });
}
}, [updateSearch, urlExtra]);
const debounceFilter = debounce((filterData: Obj) => {
if (connectUrlSearch) {
updateSearch(filterData);
}
onFilter(filterData);
}, 1000);
// filter变化的时候调用
const changeFilterData = (filterData: Obj) => {
debounceFilter(filterData);
};
const setFieldsValue = (obj: Obj) => {
const filterForm = get(filterRef, 'current.form');
if (filterForm) {
const filterFields = filterForm.getFieldsValue();
const formValue = {};
map(filterFields, (_, _key) => has(obj, _key) && set(formValue, _key, obj[_key]));
filterForm.setFieldsValue(formatFormData ? formatFormData(formValue) : formValue);
changeFilterData(obj); // 带上除filter之外的其他参数,如pageNo
}
};
const filterProps = {
onChange: {
actions: [],
},
onSubmit: {},
};
return (
<BaseFilter
onSubmit={changeFilterData}
className={`dice-filter my-3 ${className}`}
{...rest}
{...(filterProps[filterTirgger] || {})}
ref={filterRef}
/>
);
}
Example #22
Source File: resolve.ts From ui5-language-assistant with Apache License 2.0 | 5 votes |
function ignoreType(typeName: string): boolean {
return has(typesToIgnore, typeName);
}
Example #23
Source File: plugin.ts From lift with MIT License | 5 votes |
resolveReference({ address }: { address: string }): { value: string } {
return {
/**
* Construct variables are resolved lazily using the CDK's "Token" system.
* CDK Lazy values generate a unique `${Token[TOKEN.63]}` string. These strings
* can later be resolved to the real value (which we do in `initialize()`).
* Problem:
* - Lift variables need constructs to be resolved
* - Constructs can be created when Serverless variables are resolved
* - Serverless variables must resolve Lift variables
* This is a chicken and egg problem.
* Solution:
* - Serverless boots, plugins are created
* - variables are resolved
* - Lift variables are resolved to CDK tokens (`${Token[TOKEN.63]}`) via `Lazy.any(...)`
* (we can't resolve the actual values since we don't have the constructs yet)
* - `initialize` hook
* - Lift builds the constructs
* - CDK tokens are resolved into real value: we can now do that using the CDK "token resolver"
*/
value: Lazy.any({
produce: () => {
const constructs = this.getConstructs();
const [id, property] = address.split(".", 2);
if (!has(this.constructs, id)) {
throw new ServerlessError(
`No construct named '${id}' was found, the \${construct:${id}.${property}} variable is invalid.`,
"LIFT_VARIABLE_UNKNOWN_CONSTRUCT"
);
}
const construct = constructs[id];
const properties = construct.variables ? construct.variables() : {};
if (!has(properties, property)) {
if (Object.keys(properties).length === 0) {
throw new ServerlessError(
`\${construct:${id}.${property}} does not exist. The construct '${id}' does not expose any property`,
"LIFT_VARIABLE_UNKNOWN_PROPERTY"
);
}
throw new ServerlessError(
`\${construct:${id}.${property}} does not exist. Properties available on \${construct:${id}} are: ${Object.keys(
properties
).join(", ")}`,
"LIFT_VARIABLE_UNKNOWN_PROPERTY"
);
}
return properties[property];
},
}).toString(),
};
}
Example #24
Source File: convert.ts From ui5-language-assistant with Apache License 2.0 | 5 votes |
function convertLibraryToSemanticModel(
libName: string,
lib: apiJson.SchemaForApiJsonFiles,
jsonSymbols: Record<string, apiJson.ConcreteSymbol>,
strict: boolean
): model.UI5SemanticModel {
const model: model.UI5SemanticModel = {
version: lib.version,
includedLibraries: [],
classes: newMap(),
interfaces: newMap(),
enums: newMap(),
functions: newMap(),
namespaces: newMap(),
typedefs: newMap(),
};
if (lib.symbols === undefined) {
return model;
}
for (const symbol of lib.symbols) {
const fqn = symbol.name;
if (has(jsonSymbols, fqn)) {
error(
`${libName}: Duplicate symbol found: ${symbol.kind} ${fqn}. First occurrence is a ${jsonSymbols[fqn].kind}.`,
strict
);
continue;
}
jsonSymbols[fqn] = symbol;
// For some reason we get a non-reachable case branch here on all cases except "typedef", although it's not correct
// noinspection JSUnreachableSwitchBranches
switch (symbol.kind) {
case "namespace": // Fallthrough
case "member": {
model.namespaces[fqn] = convertNamespace(libName, symbol);
break;
}
case "class": {
model.classes[fqn] = convertClass(libName, symbol);
break;
}
case "enum": {
model.enums[fqn] = convertEnum(libName, symbol);
break;
}
case "function": {
model.functions[fqn] = convertFunction(libName, symbol);
break;
}
case "interface": {
model.interfaces[fqn] = convertInterface(libName, symbol);
break;
}
case "typedef": {
model.typedefs[fqn] = convertTypedef(libName, symbol);
break;
}
}
}
return model;
}
Example #25
Source File: pivot-data-set.ts From S2 with MIT License | 5 votes |
public getDimensionValues(field: string, query?: DataType): string[] {
const { rows = [], columns = [] } = this.fields || {};
let meta: PivotMeta = new Map();
let dimensions: string[] = [];
if (includes(rows, field)) {
meta = this.rowPivotMeta;
dimensions = rows;
} else if (includes(columns, field)) {
meta = this.colPivotMeta;
dimensions = columns;
}
if (!isEmpty(query)) {
let sortedMeta = [];
const dimensionValuePath = [];
for (const dimension of dimensions) {
const value = get(query, dimension);
dimensionValuePath.push(`${value}`);
const cacheKey = dimensionValuePath.join(`${ID_SEPARATOR}`);
if (meta.has(value) && !isUndefined(value)) {
const childField = meta.get(value)?.childField;
meta = meta.get(value).children;
if (
find(this.sortParams, (item) => item.sortFieldId === childField) &&
this.sortedDimensionValues[childField]
) {
const dimensionValues = this.sortedDimensionValues[
childField
]?.filter((item) => item?.includes(cacheKey));
sortedMeta = getDimensionsWithoutPathPre([...dimensionValues]);
} else {
sortedMeta = [...meta.keys()];
}
}
}
if (isEmpty(sortedMeta)) {
return [];
}
return filterUndefined(getListBySorted([...meta.keys()], sortedMeta));
}
if (this.sortedDimensionValues[field]) {
return filterUndefined(
getDimensionsWithoutPathPre([...this.sortedDimensionValues[field]]),
);
}
return filterUndefined([...meta.keys()]);
}
Example #26
Source File: primary-key.ts From dyngoose with ISC License | 5 votes |
public fromKey(hash: QueryFilters<T> | HashKeyType, range?: RangeKeyType): T {
// if the hash was passed a query filters, then extract the hash and range
if (isObject(hash) && !isDate(hash)) {
const filters = hash
if (!has(filters, this.metadata.hash.propertyName)) {
throw new QueryError('Cannot perform .get() on a PrimaryKey without specifying a hash key value')
} else if (this.metadata.range != null && !has(filters, this.metadata.range.propertyName)) {
throw new QueryError('Cannot perform .get() on a PrimaryKey with a range key without specifying a range value')
} else if (Object.keys(filters).length > 2) {
throw new QueryError('Cannot perform a .get() on a PrimaryKey with additional filters, use .query() instead')
}
hash = get(filters, this.metadata.hash.propertyName)
if (isArray(hash)) {
if (hash[0] === '=') {
hash = hash[1]
} else {
throw new QueryError('DynamoDB only supports using equal operator for the HASH key')
}
}
if (this.metadata.range != null) {
range = get(filters, this.metadata.range.propertyName)
if (isArray(hash)) {
if (hash[0] === '=') {
hash = hash[1]
} else {
throw new QueryError('DynamoDB only supports using equal operator for the RANGE key on GetItem operations')
}
}
}
}
if (this.metadata.range != null && range == null) {
throw new QueryError('Cannot use primaryKey.get without a range key value')
}
const keyMap: DynamoDB.AttributeMap = {
[this.metadata.hash.name]: this.metadata.hash.toDynamoAssert(hash),
}
if (this.metadata.range != null) {
keyMap[this.metadata.range.name] = this.metadata.range.toDynamoAssert(range)
}
return this.table.fromDynamo(keyMap, false)
}
Example #27
Source File: index.ts From prism-frontend with MIT License | 5 votes |
safeCountry =
COUNTRY && has(configMap, COUNTRY) ? (COUNTRY as Country) : DEFAULT
Example #28
Source File: search.ts From dyngoose with ISC License | 5 votes |
private checkFilters(hash: Attribute<any>, range?: Attribute<any>): boolean {
// cannot filter by a key without a value for the hash key
for (const filters of this.filters) {
if (!has(filters, hash.name)) {
continue
}
const hashFilter: Filter<any> = get(filters, hash.name)
// if there is an operator, ensure it is allowed as a key expression
if (isArray(hashFilter)) {
const operator = hashFilter[0]
if (!includes(keyConditionAllowedOperators, operator)) {
continue
}
}
// if it has no range, then we're all done
if (range == null) {
return true
}
// check for the range now
if (!has(filters, range.name)) {
continue
}
const rangeFilter: Filter<any> = get(filters, range.name)
// if there is an operator, ensure it is allowed as a key expression
if (isArray(rangeFilter)) {
const operator = rangeFilter[0]
if (!includes(keyConditionAllowedOperators, operator)) {
continue
}
}
return true
}
return false
}
Example #29
Source File: index.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
EditIssueDrawer = (props: IProps) => {
const {
id: propId,
visible,
closeDrawer,
issueType: propsIssueType,
iterationID,
projectId,
shareLink,
ticketType,
customUrl,
} = props;
const [issueType, setIssueType] = React.useState(propsIssueType);
const type = issueType.toLowerCase();
const {
getIssueDetail,
updateIssue,
updateType,
getIssueStreams,
createIssue,
copyIssue,
addIssueStream,
deleteIssue,
getFieldsByIssue,
addFieldsToIssue,
} = issueStore.effects;
const { clearIssueDetail } = issueStore.reducers;
const [bugStageList, taskTypeList, fieldList] = issueFieldStore.useStore((s) => [
s.bugStageList,
s.taskTypeList,
s.fieldList,
]);
const id = propId;
const isEditMode = !!id;
const defaultCustomFormData = React.useMemo(() => {
const customFieldDefaultValues = {};
map(fieldList, (item) => {
if (item && item.required) {
if (item.propertyType === 'Select') {
customFieldDefaultValues[item.propertyName] = item.enumeratedValues?.[0].id;
}
if (item.propertyType === 'MultiSelect') {
customFieldDefaultValues[item.propertyName] = [item.enumeratedValues?.[0].id];
}
}
});
return customFieldDefaultValues;
}, [fieldList]);
const defaultFormData = React.useMemo(() => {
return {
priority: ISSUE_PRIORITY_MAP.NORMAL.value,
complexity: ISSUE_COMPLEXITY_MAP.NORMAL.value,
severity: BUG_SEVERITY_MAP.NORMAL.value,
taskType: taskTypeList?.length ? taskTypeList[0].value : '',
bugStage: bugStageList?.length ? bugStageList[0].value : '',
assignee: userStore.getState((s) => s.loginUser.id),
planFinishedAt: issueType === ISSUE_TYPE.EPIC ? new Date() : undefined,
planStartedAt: issueType === ISSUE_TYPE.EPIC ? new Date() : undefined,
iterationID,
content: isEditMode ? '' : templateMap[issueType] || '',
...defaultCustomFormData,
};
}, [bugStageList, defaultCustomFormData, isEditMode, issueType, iterationID, taskTypeList]);
const [formData, setFormData] = React.useState(defaultFormData as any);
const drawerVisibleRef = React.useRef(visible);
const issueDetail: ISSUE.IssueType = issueStore.useStore((s) => s[`${type}Detail`]);
// 监听bugDetail、taskDetail、requirementDetail的变化,切换类型后触发刷新
issueStore.useStore((s) => [s.bugDetail, s.taskDetail, s.requirementDetail]);
const labels = labelStore.useStore((s) => s.list);
const { getLabels } = labelStore.effects;
const [updateIssueLoading] = useLoading(issueStore, ['updateIssue']);
const labelNames = map(labels, ({ name }) => name);
const [isLoading, setIsLoading] = React.useState(false);
const [hasEdited, setHasEdited] = React.useState(false);
const [tempDescContent, setTempDescContent] = React.useState('');
const [disableSubmit, setDisableSubmit] = React.useState(false);
const isBug = issueType === ISSUE_TYPE.BUG;
const customFieldDetail = issueStore.useStore((s) => s.customFieldDetail);
const [customFormData, setCustomFormData] = React.useState(customFieldDetail as any);
const { getFieldsByIssue: getCustomFieldsByProject } = issueFieldStore.effects;
React.useEffect(() => {
if (!labels?.length) {
getLabels({ type: 'issue' });
}
}, [labels, getLabels]);
const savingRef = React.useRef(false);
const isBacklog = iterationID === -1;
const isMonitorTicket = ticketType === 'monitor';
const { creator, assignee, testPlanCaseRels } = issueDetail || {};
const specialProps = EDIT_PROPS[issueType];
const projectPerm = usePerm((s) => s.project);
const permObjMap = {
[ISSUE_TYPE.REQUIREMENT]: projectPerm.requirement,
[ISSUE_TYPE.TASK]: projectPerm.task,
[ISSUE_TYPE.BUG]: projectPerm.bug,
[ISSUE_TYPE.TICKET]: projectPerm.ticket,
[ISSUE_TYPE.EPIC]: projectPerm.epic,
};
const permObj = permObjMap[issueType];
const checkRole = [isCreator(creator), isAssignee(assignee)];
const deleteAuth = isMonitorTicket ? true : getAuth(permObj.delete, checkRole);
const createAuth = permObj.create.pass;
const editAuth = isMonitorTicket ? true : !isEditMode || getAuth(permObj.edit, checkRole);
const switchTypeAuth = getAuth(permObj.switchType, checkRole);
const addRelatedMattersProjectId = routeInfoStore.getState((s) => s.params).projectId;
const { addIssueRelation } = issueStore.effects;
const { updateCustomFieldDetail } = issueStore.reducers;
const { id: orgID } = orgStore.useStore((s) => s.currentOrg);
const metaFieldsRef: React.RefObject<unknown> = React.useRef(null);
const [tempStateData, setTempStateData] = React.useState('');
React.useEffect(() => {
setFormData((prev: any) => ({ ...prev, iterationID }));
}, [iterationID]);
const getCustomFields = React.useCallback(() => {
id && getFieldsByIssue({ issueID: id, propertyIssueType: issueType, orgID });
}, [getFieldsByIssue, id, issueType, orgID]);
React.useEffect(() => {
setIssueType(propsIssueType);
}, [propsIssueType]);
React.useEffect(() => {
drawerVisibleRef.current = visible;
}, [visible]);
React.useEffect(() => {
if (visible) {
if (id) {
getIssueStreams({ type: issueType, id, pageNo: 1, pageSize: 100 });
getCustomFields();
}
getCustomFieldsByProject({
propertyIssueType: issueType,
orgID,
}).then((res) => {
updateCustomFieldDetail({
property: res,
orgID,
projectID: +addRelatedMattersProjectId,
issueID: undefined,
});
});
}
}, [
addRelatedMattersProjectId,
getCustomFields,
getCustomFieldsByProject,
getFieldsByIssue,
getIssueDetail,
getIssueStreams,
id,
issueType,
orgID,
updateCustomFieldDetail,
visible,
]);
const customFieldValues = React.useMemo(() => {
customFieldDetail && setCustomFormData(customFieldDetail);
const tempFormData = {};
map(customFieldDetail?.property, (item) => {
const { arbitraryValue, propertyType, values, propertyName } = item;
const _values = values || [];
tempFormData[propertyName] = FIELD_WITH_OPTION[propertyType]
? propertyType === 'MultiSelect'
? _values
: _values[0]
: arbitraryValue;
});
return tempFormData;
}, [customFieldDetail]);
React.useEffect(() => {
issueDetail && setFormData({ ...issueDetail, ...customFieldValues });
}, [customFieldValues, issueDetail]);
const dataCheck = (_data: Obj) => {
if (ISSUE_TYPE.TASK === issueType) {
// 创建时任务必填预估工时, 任务类型
if (!isEditMode && !_data.taskType && issueType === ISSUE_TYPE.TASK) {
message.warn(i18n.t('Task type'));
return false;
}
// if (!isEditMode && !_data.issueManHour?.estimateTime) {
// message.warn(i18n.t('dop:missing estimateTime'));
// return false;
// }
}
if (!_data.title) {
message.warn(i18n.t('dop:missing title'));
return false;
}
if (!_data.assignee) {
message.warn(i18n.t('dop:missing assignee'));
return false;
}
// if (!_data.iterationID) {
// message.warn(i18n.t('please choose the {name}', { name: i18n.t('dop:Iteration-owned') }));
// return false;
// }
if (ISSUE_TYPE.BUG === issueType) {
if (!_data.bugStage) {
message.warn(i18n.t('dop:missing import source'));
return false;
}
}
if (!_data.planFinishedAt && ISSUE_TYPE.EPIC === issueType) {
message.warn(i18n.t('dop:missing deadline'));
return false;
}
if (!_data.planStartedAt && ISSUE_TYPE.EPIC === issueType) {
message.warn(i18n.t('dop:missing startTime'));
return false;
}
return true;
};
const checkFieldNotEmpty = (propertyType: ISSUE_FIELD.IPropertyType, propertyValue: any) => {
if (propertyType === 'MultiSelect') {
return !isEmpty(propertyValue);
} else if (propertyType === 'Number') {
return propertyValue || String(propertyValue) === '0';
} else {
return propertyValue;
}
};
const checkCustomFormData = (filterKey?: string) => {
if (customFormData?.property) {
const tempList = customFormData.property;
for (let i = 0, len = tempList.length; i < len; i++) {
const { displayName, required, arbitraryValue, propertyType, values, propertyName } = tempList[i];
const _values = values || [];
let propertyValue = [];
if (propertyType === 'MultiSelect') {
propertyValue = _values;
} else if (propertyType === 'Select') {
propertyValue = _values.length ? [_values[0]] : [];
} else {
propertyValue = arbitraryValue || String(arbitraryValue) === '0' ? [arbitraryValue] : [];
}
if (isEmpty(propertyValue) && required && (!filterKey || filterKey !== propertyName)) {
message.warn(i18n.t('missing {name}', { name: displayName }));
return false;
}
}
}
return true;
};
const focusOnFields = (fieldKey: string) => {
metaFieldsRef?.current?.onFocus(fieldKey);
};
const setField = (value: Obj<any>) => {
const formattedValue = value;
// 处理 issueManHour
if (has(value, 'issueManHour')) {
formattedValue.issueManHour = merge({}, formData.issueManHour, value.issueManHour);
}
const params: ISSUE.IssueType = merge({}, formData, formattedValue);
if (value.labels) {
params.labels = value.labels;
}
if (has(value, 'planFinishedAt') && !value.planFinishedAt) {
params.planFinishedAt = ''; // replace null to mark delete
}
if (has(value, 'planStartedAt') && !value.planStartedAt) {
params.planStartedAt = '';
}
if ([ISSUE_TYPE.TASK, ISSUE_TYPE.BUG].includes(issueType)) {
const warnMessage = [];
if (value.state && isEditMode) {
setTempStateData(value.state);
// 编辑模式下修改状态时,必填时间追踪和预估工时, 任务类型
if (!params.taskType && issueType === ISSUE_TYPE.TASK) {
warnMessage.push({ msg: i18n.t('dop:missing task type'), key: 'taskType' });
}
if (!params.issueManHour.estimateTime) {
warnMessage.push({ msg: i18n.t('dop:Estimated time'), key: 'issueManHour.estimateTime' });
}
if (params.issueManHour.elapsedTime === 0 && params.issueManHour.thisElapsedTime === 0) {
// filter out the working
const workingState = formData.issueButton.find((item) => item.stateBelong === 'WORKING');
// When working exists and select working, don't warn
if (!workingState || value.state !== workingState.stateID) {
warnMessage.push({ msg: i18n.t('dop:time spent in time tracing'), key: 'issueManHour.elapsedTime' });
}
}
}
if (warnMessage.length !== 0) {
message.warn(
<>
<span className="font-bold">{map(warnMessage, 'msg').join(', ')}</span>
<span>{i18n.t('dop:missing')}</span>
</>,
);
focusOnFields(warnMessage[0].key);
return false;
}
}
// after validation, then set temp state in data. prevent enter line 857. see erda bug #235076
if (has(value, 'issueManHour') && tempStateData) {
formattedValue.state = tempStateData;
setTempStateData('');
}
let promise;
let customFieldKey = '';
let customFieldValue: any;
map(Object.keys(value), (k) => {
customFieldKey = k;
if (!(['planFinishedAt', 'planStartedAt'].includes(k) && !value[k])) {
params[k] = value[k];
}
customFieldValue = value[k];
});
const customFieldData = find(customFormData?.property, (item) => item.propertyName === customFieldKey);
const tempCustomFormData: ISSUE.ICreateField = produce(customFormData, (draft: any) => {
map(draft?.property, (draftData) => {
if (draftData.propertyName === customFieldKey) {
if (FIELD_WITH_OPTION[draftData?.propertyType]) {
const _values = customFieldValue || [];
// eslint-disable-next-line no-param-reassign
draftData.values = Array.isArray(_values) ? _values : [_values];
} else if (draftData?.propertyType === 'Number') {
// eslint-disable-next-line no-param-reassign
draftData.arbitraryValue = Number(customFieldValue);
} else {
// eslint-disable-next-line no-param-reassign
draftData.arbitraryValue = customFieldValue;
}
}
});
});
setCustomFormData(tempCustomFormData);
if (isEditMode) {
setHasEdited(true);
if (dataCheck({ ...params, customUrl })) {
params.iterationID = +(params.iterationID as number) || -1;
if (tempDescContent) {
params.content = tempDescContent;
}
if (!customFieldValues?.hasOwnProperty(customFieldKey)) {
savingRef.current = true;
promise = updateIssue({ ...params, customUrl })
.then(() => {
getIssueStreams({ type: issueType, id: id as number, pageNo: 1, pageSize: 100 });
getIssueDetail({ id: id as number }).then(() => {
savingRef.current = false;
});
// setHasEdited(false); // 更新后置为false
return true;
})
.catch(() => {
savingRef.current = false;
return false;
});
} else {
addFieldsToIssue(
{ ...tempCustomFormData, orgID, projectID: +addRelatedMattersProjectId },
{ customMsg: i18n.t('updated successfully') },
).then(() => {
getCustomFields();
});
}
}
if (!checkFieldNotEmpty(customFieldData?.propertyType, customFieldValue) && customFieldData?.required) {
const name = customFieldData?.displayName;
message.warn(i18n.t('missing {name}', { name }));
focusOnFields(name);
return;
}
if (!checkCustomFormData(customFieldKey)) return;
}
setFormData(params);
return promise;
};
const setFieldCb = (value: Obj<any>, fieldType?: string) => {
if (!drawerVisibleRef.current) {
return;
}
if (fieldType && fieldType === 'markdown') {
setTempDescContent(value?.content);
return;
}
if (value.labels) {
const labelName = cloneDeep(value.labels).pop();
if (isEmpty(value.labels) || includes(labelNames, labelName)) {
return setField(value);
} else {
message.info(i18n.t('dop:the label does not exist, please select again'));
return setField({ ...value, labels: value.labels.slice(0, -1) }); // remove the last label, which is not exist
}
} else {
return setField(value);
}
};
const onClose = (isCreate = false, isDelete = false) => {
if (savingRef.current) return;
drawerVisibleRef.current = false;
setFormData(defaultFormData);
closeDrawer({ hasEdited, isCreate, isDelete });
setTempDescContent('');
setIssueType(propsIssueType);
setHasEdited(false);
setIsLoading(false);
updateSearch({
id: undefined,
});
setCustomFormData(customFieldDetail);
isEditMode && issueType && clearIssueDetail(issueType);
};
const onDelete = () => {
id &&
deleteIssue(id).then(() => {
onClose(false, true);
});
};
const handleSubmit = (isCopy = false, copyTitle = '') => {
if (!dataCheck(formData)) return;
if (!checkCustomFormData()) return;
setDisableSubmit(true);
const params: ISSUE.IssueType = {
projectID: Number(projectId),
iterationID, // keep it first, allow form overwrite iterationID
...formData,
type: issueType,
};
params.iterationID = +(params.iterationID as number) || -1; // 需求池指定迭代为-1
isBug && !(params as ISSUE.Bug).owner && ((params as ISSUE.Bug).owner = params.assignee); // 责任人暂时默认设为处理人
if (isCopy) {
const { creator, ...restFormData } = formData;
copyIssue({
...restFormData,
title: copyTitle,
issueManHour: { ...formData.issueManHour, elapsedTime: undefined },
customUrl,
})
.then((res) => {
addFieldsToIssue(
{ ...customFormData, issueID: res, projectID: params.projectID },
{ customMsg: i18n.t('copied successfully') },
);
onClose(true);
})
.finally(() => {
setDisableSubmit(false);
});
return;
}
if (id) {
updateIssue({
...formData,
issueManHour: { ...formData.issueManHour, elapsedTime: undefined },
customUrl,
}).then(() => {
onClose();
});
} else {
createIssue({ ...params, customUrl }, { hideActionMsg: true })
.then((res) => {
savingRef.current = false;
addFieldsToIssue(
{ ...customFormData, issueID: res, projectID: params.projectID },
{ customMsg: i18n.t('created successfully') },
);
onClose(true);
})
.finally(() => {
setDisableSubmit(false);
});
}
};
const switchType = (currentType: string) => {
setIsLoading(true);
updateType({ id: formData.id, type: currentType }).then(() => {
setHasEdited(true);
setIssueType(currentType as ISSUE_TYPE);
setIsLoading(false);
});
};
const handleMenuClick = ({ key }: { key: string }) => {
iterationStore.effects
.createIssue({
// 创建事件
projectID: +addRelatedMattersProjectId,
iterationID: -1,
type: key,
priority: 'NORMAL',
title: issueDetail.title,
content: issueDetail.content,
})
.then((res: number) => {
addRelation(res); // 添加关联
});
};
const addRelation = (val: number) => {
addIssueRelation({
relatedIssues: [val],
id: issueDetail.id,
projectId: +addRelatedMattersProjectId,
type: 'connection',
}).then(() => {
setHasEdited(true);
getIssueRelation.fetch({
issueId: issueDetail.id,
});
});
};
const addQuickIssueAuth = usePerm((s) => s.project.requirement.create.pass); // 目前迭代、任务、缺陷添加权限都一致
let extraHeaderOp: JSX.Element | React.ElementType | null = null;
if (isEditMode && issueType === ISSUE_TYPE.TICKET) {
if (addQuickIssueAuth) {
extraHeaderOp = (
<Dropdown
key="quick-add"
overlay={
<Menu onClick={handleMenuClick}>
<Menu.Item key="REQUIREMENT">
<IssueIcon type="REQUIREMENT" withName />
</Menu.Item>
<Menu.Item key="TASK">
<IssueIcon type="TASK" withName />
</Menu.Item>
<Menu.Item key="BUG">
<IssueIcon type="BUG" withName />
</Menu.Item>
</Menu>
}
>
<Button size="small" className="mr-2 shadow-none">
{i18n.t('dop:One Click to Backlog')}
</Button>
</Dropdown>
);
} else {
extraHeaderOp = (
<WithAuth key="create" pass={addQuickIssueAuth}>
<Button size="small" className="mr-2 shadow-none">
{i18n.t('dop:One Click to Backlog')}
</Button>
</WithAuth>
);
}
}
let footer;
if (!isEditMode) {
footer = (isChanged: boolean, confirmCloseTip: string | undefined) => (
<Spin key="submit" spinning={updateIssueLoading}>
<div className="pl-8 py-2 space-x-2 border-solid border-transparent border-top">
<Button disabled={disableSubmit} onClick={() => handleSubmit()} type="primary">
{i18n.t('OK')}
</Button>
{isChanged && confirmCloseTip ? (
<Popconfirm title={confirmCloseTip} placement="topLeft" onConfirm={() => onClose()}>
<Button>{i18n.t('Cancel')}</Button>
</Popconfirm>
) : (
<Button onClick={() => onClose()}>{i18n.t('Cancel')}</Button>
)}
</div>
</Spin>
);
} else {
footer = issueDetail ? (
<IssueCommentBox
onSave={(content) => {
addIssueStream(issueDetail, { content });
emit('issue:scrollToLatestComment');
}}
editAuth={editAuth}
/>
) : null;
}
const relationData = getIssueRelation.useData();
return (
<IssueDrawer
editMode={isEditMode}
visible={visible}
loading={isLoading || updateIssueLoading}
onClose={() => onClose()}
onDelete={isEditMode ? onDelete : undefined}
shareLink={shareLink}
canDelete={deleteAuth && !isMonitorTicket}
canCreate={createAuth}
confirmCloseTip={isEditMode ? undefined : i18n.t('dop:The new data will be lost if closed. Continue?')}
handleCopy={handleSubmit}
maskClosable={isEditMode}
data={formData}
projectId={projectId}
issueType={issueType}
setData={setFormData}
footer={footer}
header={(scrollY) => (
<div className="flex items-center">
<IF check={isEditMode}>
{[ISSUE_TYPE.REQUIREMENT, ISSUE_TYPE.TASK, ISSUE_TYPE.BUG].includes(issueType) ? (
<WithAuth pass={switchTypeAuth}>
<EditField
name="type"
type="select"
data={formData}
itemProps={{
options: getIssueTypeOption(),
optionLabelProp: 'data-icon',
dropdownMatchSelectWidth: false,
allowClear: false,
showArrow: true,
size: 'small',
className: 'switch-type-selector bg-default-06',
style: { width: 60 },
getPopupContainer: () => document.body,
}}
onChangeCb={(field: any) => {
if (field.type !== issueType) {
switchType(field.type);
}
}}
/>
</WithAuth>
) : (
<span className="mr-2 flex items-center h-full">{ISSUE_TYPE_MAP[issueType]?.icon}</span>
)}
<IF.ELSE />
<IssueIcon type={issueType} withName />
</IF>
<div className={`ml-2 issue-header-title-wrap ${scrollY > 20 ? 'slide-up' : ''}`}>
<div className="issue-header-title">
<div className="flex items-center h-7">
{relationData?.beIncluded?.[0] && (
<>
<ErdaIcon className="mx-2 text-sub" type="right" size="16px" />
<a
href={`${location.href.split('?')[0]}?${mergeSearch(
{
id: relationData.beIncluded[0].id,
type: relationData.beIncluded[0].type,
tab: 'ALL',
},
true,
)}`}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center bg-default-06 rounded-sm px-2 hover:text-purple-deep overflow-hidden cursor-pointer"
style={{ maxWidth: '320px' }}
>
<ErdaIcon className="mr-1" type="xuqiu" size="20px" />
<span className="flex-1 truncate">{relationData?.beIncluded?.[0].title}</span>
</a>
</>
)}
</div>
<div className="truncate text-base font-medium leading-7" style={{ maxWidth: '480px' }}>
{issueDetail?.title}
</div>
</div>
</div>
</div>
)}
extraHeaderOp={extraHeaderOp}
>
<div className="mt-1">
<EditField
name="title"
onChangeCb={setFieldCb}
data={formData}
disabled={!editAuth}
className="flex-1 mt-2 ml-[-8px] mr-[-8px]"
itemProps={{
autoFocus: !isEditMode,
className: 'text-xl text-normal px-2 font-medium',
maxLength: 255,
placeholder: firstCharToUpper(specialProps.titlePlaceHolder),
}}
/>
<IssueMetaFields
ref={metaFieldsRef}
projectId={projectId}
labels={labels}
isEditMode={isEditMode}
issueType={issueType}
isBacklog={isBacklog}
ticketType={ticketType}
editAuth={editAuth}
formData={formData}
setFieldCb={setFieldCb}
/>
</div>
<EditField
name="content"
disabled={!editAuth}
type="markdown"
onChangeCb={setFieldCb}
itemProps={{
placeHolder: i18n.t('dop:No content'),
className: 'w-full',
hasEdited,
isEditMode,
maxLength: 3000,
defaultMode: isEditMode ? 'html' : 'md',
}} // 编辑时默认显示预览
data={formData}
/>
<Choose>
<When condition={[ISSUE_TYPE.TASK].includes(issueType) && isEditMode}>
<IssueWorkflow projectID={+addRelatedMattersProjectId} id={propId!} type={issueType} metaIssue={formData} />
</When>
<Otherwise>{IssueDrawer.Empty}</Otherwise>
</Choose>
<Choose>
<When condition={isEditMode && !!issueDetail && issueType === ISSUE_TYPE.REQUIREMENT}>
<IssueInclusion issueDetail={issueDetail} iterationID={iterationID} setHasEdited={setHasEdited} />
</When>
<Otherwise>{IssueDrawer.Empty}</Otherwise>
</Choose>
<Choose>
<When condition={isEditMode && !!issueDetail}>
<IssueConnection
editAuth={editAuth}
issueDetail={issueDetail}
iterationID={iterationID}
setHasEdited={setHasEdited}
/>
</When>
<Otherwise>{IssueDrawer.Empty}</Otherwise>
</Choose>
<Choose>
<When condition={isEditMode && !!issueDetail}>
<IssueActivities type={issueType} />
</When>
<Otherwise>{IssueDrawer.Empty}</Otherwise>
</Choose>
</IssueDrawer>
);
}